|
|
逆袭的黑框眼镜 · PolarDB ...· 1 年前 · |
|
|
爱搭讪的开水瓶 · Linux 变量的使用 - 刘合栋 - 博客园· 2 年前 · |
|
|
纯真的丝瓜 · NA、Inf、NaN、NULL等值处理-腾讯 ...· 2 年前 · |
|
|
正直的酸菜鱼 · js模拟键盘某个键按下_教你认识电脑键盘上的 ...· 2 年前 · |
|
|
八块腹肌的遥控器 · .基于ANSYS_Workbench ...· 2 年前 · |
```js
props:{
name:{
type:String, //类型
required:true, //必要性
default:'老王' //默认值
> 备注:props是只读的,Vue底层会监测你对props的修改,如果进行了修改,就会发出警告,若业务需求确实需要修改,那么请复制props的内容到data中一份,然后去修改data中的数据。
mixin(混入)
1. 功能:可以把多个组件共用的配置提取成一个混入对象
2. 使用方式:
第一步定义混合:
data(){....},
methods:{....}
第二步使用混入:
全局混入:```Vue.mixin(xxx)```
局部混入:```mixins:['xxx'] ```
1. 功能:用于增强Vue
npm i vue-resource
mian.ts中引入和使用
// 引入Vue
import Vue from 'vue'
// 引入App
import App from './App.vue'
Vue.config.productionTip = false
// 引入插件plugins.js
import plugins from './plugins'
// 关闭Vue的生产提示
Vue.config.productionTip = false
// 引用插件
Vue.use(plugins)
//应用(使用)插件 install(Vue,x,y,z)
Vue.use(plugins,1,2,3)
// 创建vm
new Vue({
render: h => h(App)
}).$mount('#app')
2. 本质:包含install方法的一个对象,install的第一个参数是Vue,第二个以后的参数是插件使用者传递的数据。
3. 定义插件: plugins.js
```js
对象.install = function (Vue, options) {
// 1. 添加全局过滤器
Vue.filter(....)
// 2. 添加全局指令
Vue.directive(....)
// 3. 配置全局混入(合)
Vue.mixin(....)
// 4. 添加实例方法
Vue.prototype.$myMethod = function () {...}
Vue.prototype.$myProperty = xxxx
1. 组件化编码流程:
(1).拆分静态组件:组件要按照功能点拆分,命名不要与html元素冲突。
(2).实现动态组件:考虑好数据的存放位置,数据是一个组件在用,还是一些组件在用:
1).一个组件在用:放在组件自身即可。
2). 一些组件在用:放在他们共同的父组件上(<span style="color:red">状态提升</span>)。
(3).实现交互:从绑定事件开始。
2. props适用于:
(1).父组件 ==> 子组件 通信
(2).子组件 ==> 父组件 通信(要求父先给子一个函数)
3. 使用v-model时要切记:v-model绑定的值不能是props传过来的值,因为props是不可以修改的!
4. props传过来的若是对象类型的值,修改对象中的属性时Vue不会报错,但不推荐这样做。
webStorage
1. 存储内容大小一般支持5MB左右(不同浏览器可能还不一样)
2. 浏览器端通过 Window.sessionStorage 和 Window.localStorage 属性来实现本地存储机制。
3. 相关API:
1. ```xxxxxStorage.setItem('key', 'value');```
该方法接受一个键和值作为参数,会把键值对添加到存储中,如果键名存在,则更新其对应的值。
2. ```xxxxxStorage.getItem('person');```
该方法接受一个键名作为参数,返回键名对应的值。
3. ```xxxxxStorage.removeItem('key');```
该方法接受一个键名作为参数,并把该键名从存储中删除。
4. ``` xxxxxStorage.clear()```
该方法会清空存储中的所有数据。
4. 备注:
1. SessionStorage存储的内容会随着浏览器窗口关闭而消失。
2. LocalStorage存储的内容,需要手动清除才会消失。
3. ```xxxxxStorage.getItem(xxx)```如果xxx对应的value获取不到,那么getItem的返回值是null。
4. ```JSON.parse(null)```的结果依然是null。
组件的自定义事件
1. 一种组件间通信的方式,适用于:子组件 ===> 父组件
2. 使用场景:A是父组件,B是子组件,B想给A传数据,那么就要在A中给B绑定自定义事件(事件的回调在A中)。
3. 绑定自定义事件:
1. 第一种方式,在父组件中:```<Demo @atguigu="test"/>``` 或 ```<Demo v-on:atguigu="test"/>```
2. 第二种方式,在父组件中:
```js
<Demo ref="demo"/>
......
mounted(){
this.$refs.xxx.$on('atguigu',this.test)
3. 若想让自定义事件只能触发一次,可以使用```once```修饰符,或```$once```方法。
4. 触发自定义事件:```this.$emit('atguigu',数据)```
5. 解绑自定义事件```this.$off('atguigu')```
6. 组件上也可以绑定原生DOM事件,需要使用```native```修饰符。
7. 注意:通过```this.$refs.xxx.$on('atguigu',回调)```绑定自定义事件时,回调要么配置在methods中,要么用箭头函数,否则this指向会出问题!
全局事件总线(GlobalEventBus):任意组件事件之间的传递,如爷孙组件
1. 一种组件间通信的方式,适用于任意组件间通信。this.$bus.
2. 安装全局事件总线:
```js
new Vue({
......
beforeCreate() {
Vue.prototype.$bus = this //安装全局事件总线,$bus就是当前应用的vm
......
3. 使用事件总线:
1. 接收数据:A组件想接收数据,则在A组件中给$bus绑定自定义事件,事件的回调留在A组件自身。
```js
methods(){
demo(data){......}
......
mounted() {
this.$bus.$on('xxxx',this.demo) // $bus总线.$on绑定事件(‘事件名’,触发调用函数)
} ```
2. $emit提供数据:```this.$bus.$emit('xxxx',数据)```
item.vue中需触发事件
methods: {
//勾选or取消勾选
handleCheck(id){
// 通知App组件将对应的todo对象的done值取反
// 数据在App中,前往App
// this.checkTodo(id)
// 总线: 触发事件
this.$bus.$emit('checkTodo',id)
// 删除
handleDelete(id){
if (confirm('确定删除吗?')){
// this.deleteTodo(id)
// 总线: 触发事件
this.$bus.$emit('deleteTodo',id)
4. 最好在beforeDestroy钩子中,用$off去解绑当前组件所用到的事件。销毁组件事件绑定注:必须带事件名参数,不然销毁所有组件this.$bus.$off('事件名')
app.vue
// 总线开启:接收item.vue组件传参,使用钩子$on和$off完整写法
mounted(){
// $bus总线.$on绑定事件(‘事件名’,触发调用函数)
this.$bus.$on('checkTodo',this.checkTodo)
this.$bus.$on('deleteTodo',this.deleteTodo)
// 总线.$off解绑时间
beforeDestroy(){
this.$bus.$off('checkTodo')
this.$bus.$off('deleteTodo')
总线:场景更适应爷孙深层次组件事件之间的传递。
消息订阅与发布(pubsub)
1. 一种组件间通信的方式,适用于任意组件间通信。(需要数据的地方是订阅消息,提供数据的地方是发布消息)
2. 使用步骤:
1. 安装pubsub:```npm i pubsub-js```
2. 引入: ```import pubsub from 'pubsub-js'```,一般组件挂载完后进行订阅,mounted(){}
3. 接收数据:A组件想接收数据,则在A组件中订阅消息,订阅的回调留在A组件自身。
```js
methods(){
demo(data){......}
......
mounted() {
this.pid = pubsub.subscribe('xxx',this.demo) //订阅消息
4. 提供数据:```pubsub.publish('xxx',数据)```
5. 最好在beforeDestroy钩子中,用```PubSub.unsubscribe(pid)```去取消订阅。
注:相对于总线和消息订阅实现功能,Vue推荐使用总线
nextTick :实际开发中使用较多
1. 语法:```this.$nextTick(回调函数)```
<template>
<label>
<input type="checkbox" :checked="todo.done" @change="handleCheck(todo.id)"/>
<!-- 如下代码也能实现功能,但是不太推荐,因为有点违反原则,因为修改了props -->
<!-- <input type="checkbox" v-model="todo.done"/> -->
<span v-show="!todo.isEdit">{{todo.title}}</span>
<!--@blur='handleBlur('数据,新数据')’-->
<input
type="text"
v-show="todo.isEdit"
:value="todo.title"
@blur="handleBlur(todo,$event)"
ref="inputTitle" //使用nextTick
</label>
<button class="btn btn-danger" @click="handleDelete(todo.id)">删除</button>
<button v-show="!todo.isEdit" class="btn btn-edit" @click="handleEdit(todo)">编辑</button>
</template>
<script>
// 引入消息订阅pubsub:发布
import pubsub from 'pubsub-js'
export default {
name:'MyItem'
,
//声明接收todo
props:['todo'],
methods: {
//勾选or取消勾选
handleCheck(id){
//通知App组件将对应的todo对象的done值取反
// this.checkTodo(id)
this.$bus.$emit('checkTodo',id)
//删除
handleDelete(id){
if(confirm('确定删除吗?')){
//通知App组件将对应的todo对象删除
// this.deleteTodo(id)
// this.$bus.$emit('deleteTodo',id)
// publish发布消息
pubsub.publish('deleteTodo',id)
// 编辑状态
handleEdit(todo){
// 使用hasOwenProperty('isEdit')判断自身是否有isEdit属性
if (todo.hasOwnProperty('isEdit')){
todo.isEdit = true
}else {
// console.log('@')
this.$set(todo,'isEdit',true)
// nextTick:下一次 DOM 更新结束后执行
// 当改变数据后,要基于更新后的新DOM进行某些操作时,要在nextTick所指定的回调函数中执行
this.$nextTick(function (){
this.$refs.inputTitle.focus()
// 失去焦点回调(真正执行修改逻辑):编辑状态结束
// 接收@blur='handleBlur('数据,新数据')’
handleBlur(todo,e){
todo.isEdit = false
if(!e.target.value.trim()) return alert('输入不能为空!')
// 总线:提交新数据,$emit('事件名','数据_id','修改的新数据')
this.$bus.$emit('upDataTodo',todo.id,e.target.value)
</script>
2. 作用:在下一次 DOM 更新结束后执行其指定的回调。 nextTick所指定的回调会在下一次 DOM 更新结束后执行
3. 什么时候用:当改变数据后,要基于更新后的新DOM进行某些操作时,要在nextTick所指定的回调函数中执行。
定时器虽然也能达到效果,但推荐使用nextTick
Vue封装的过度与动画
1. 作用:在插入、更新或移除 DOM元素时,在合适的时候给元素添加样式类名。
2. 图示:<img src="https://img04.sogoucdn.com/app/a/100520146/5990c1dff7dc7a8fb3b34b4462bd0105" style="width:60%" />
3. 写法:
1. 准备好样式:
- 元素进入的样式:
1. v-enter:进入的起点
2. v-enter-active:进入过程中
3. v-enter-to:进入的终点
- 元素离开的样式:
1. v-leave:离开的起点
2. v-leave-active:离开过程中
3. v-leave-to:离开的终点
2. 使用```<transition>```包裹要过度的元素,并配置name属性:
```vue
<transition name="hello">
<h1 v-show="isShow">你好啊!</h1>
</transition>
Test.vue完整代码:
<!--使用过渡动画-->
<template>
<button @click="isShow = !isShow">显示/隐藏</button>
<!--过渡标签:transition -->
<!--过渡标签自定义name='hello':style中 v-变更hello-enter-active-->
<!--:appear='true'缺省写法 ,直接 appear默认-->
<!--<transition name="hello" :appear="true">-->
<transition name="hello" appear>
<h1 v-show="isShow" >你好啊</h1>
</transition>
</div>
</template>
<script>
export default {
name: 'Test',
data () {
return {
isShow:true
</script>
<style scoped>
background-color: orange;
/*使用过渡标签后自动动画,Vue特定:v-enter-active*/
/*vue-enter进入-active激活*/
/*.v-enter-active {
!*css3动画来:匀速*!
animation: atguigu 1s linear;
/*<!--过渡标签自定义name='hello':style中 v-变更hello-enter-active-->*/
.hello-enter-active {
animation: atguigu 1s linear;
/*
使用过渡标签后自动动画,Vue特定:v-leave-active*/
/*vue-leave离开-active激活*/
/*<!--过渡标签自定义name='hello':style中 v-变更hello-leave-active-->*/
/*.v-leave-active {*/
.hello-leave-active {
/*css3动画离开*/
/*播放动画:atguigu 1秒 reverse翻转*/
animation: atguigu 1s reverse;
/*定义动画关键帧*/
@keyframes atguigu {
from{
transform: translateX(-100%);
transform: translateY(0px);
</style>
3. 备注:若有多个元素需要过度,则需要使用:```<transition-group>```,且每个元素都要指定```key```值。
Test2.vue完整代码:
<!--不使用过渡动画-->
<template>
<button @click="isShow = !isShow">显示/隐藏</button>
<!--过渡标签:transition -->
<!--过渡标签自定义name='hello':style中 v-变更hello-enter-active-->
<!--:appear='true'缺省写法 ,直接 appear默认-->
<!--<transition name="hello" :appear="true">-->
<transition name="hello" appear>
<h1 v-show="isShow" >你好啊</h1>
</transition>
<!--有多个元素需要过度,则需要使用:```<transition-group>```,且每个元素都要指定```key```值-->
<!--使用场景:如效果取反时-->
<transition-group name="hello" appear>
<h1 v-show="!isShow" key="1">你好啊!</h1>
<h1 v-show="isShow" key="2">尚硅谷</h1>
</transition-group>
</div>
</template>
<script>
export default {
name: 'Test',
data () {
return {
isShow:true
</script>
<style scoped>
/*破坏原有h1效果:transition */
background-color: orange;
/*transition: 0.5s linear;*/
/*进入的起点,离开的终点*/
.hello-enter,.hello-leave-to {
transform: translateX(-100%);
/*避免破坏h1原有代码:*/
.hello-enter-active,.hello-leave-active {
transition: 0.5s linear;
/*进入的终点、离开的起点*/
.hello-enter-to,.hello-leave {
transform: translateX(0);
</style>
使用animate.css第三方库方法
安装:npm install -y animate.css --save
Test3.vue完整代码
<!--使用animate.css第三方库-->
<template>
<button @click="isShow = !isShow">显示/隐藏</button>
<!--过渡标签:transition -->
<!--过渡标签自定义name='hello':style中 v-变更hello-enter-active-->
<!--:appear='true'缺省写法 ,直接 appear默认-->
<!--<transition name="hello" :appear="true">-->
<!--<transition name="hello" appear>
<h1 v-show="isShow" >你好啊</h1>
</transition>-->
<!--有多个元素需要过度,则需要使用:```<transition-group>```,且每个元素都要指定```key```值-->
<!--使用场景:如效果取反时-->
<!--使用第三方animate.css时,按官网配置name='animate__animated animate__bounce'
enter-active-class="效果名animate__swing"
leave-active-class="效果名animate__backOutUp" 三个。
<transition-group
name="animate__animated animate__bounce"
enter-active-class="animate__swing"
leave-active-class="animate__backOutUp"
appear>
<h1 v-show="!isShow" key="1">你好啊!</h1>
<h1 v-show="isShow" key="2">尚硅谷</h1>
</transition-group>
</div>
</template>
<script>
//
引入第三方animate.css库
import 'animate.css'
export default {
name: 'Test',
data () {
return {
isShow:true
</script>
<style scoped>
/*破坏原有h1效果:transition */
background-color: orange;
/*transition: 0.5s linear;*/
/*使用第三库时,以下省略不写*/
!*进入的起点,离开的终点*!
.hello-enter,.hello-leave-to {
transform: translateX(-100%);
!*避免破坏h1原有代码:*!
.hello-enter-active,.hello-leave-active {
transition: 0.5s linear;
!*进入的终点、离开的起点*!
.hello-enter-to,.hello-leave {
transform: translateX(0);
</style>
vue脚手架配置代理
Vue中推荐使用ajax库: Axios库
npm i axios
在vue.config.js中添加如下配置:
const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
transpileDependencies: true,
lintOnSave: false, // 关闭语法检查
// 开启代理服务器:端口5000
// 弊端1:不能配置多个代理,只能唯一
// 弊端2:请求本地无时,才走代理服务器
devServer:{
proxy:"http://localhost:5000"
1. 优点:配置简单,请求资源时直接发给前端(8080)即可。
2. 缺点:不能配置多个代理,不能灵活的控制请求是否走代理。
3. 工作方式:若按照上述配置代理,当请求了前端不存在的资源时,那么该请求会转发给服务器 (优先匹配前端资源)
编写vue.config.js配置具体代理规则:
const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
transpileDependencies: true,
lintOnSave: false, // 关闭语法检查
// 方式一
// 开启代理服务器:端口5000
// 弊端1:不能配置多个代理,只能唯一
// 弊端2:请求本地无时,才走代理服务器
/*devServer:{
proxy:"http://localhost:5000"
},*/
// 方式二: 官网devServer.proxy中
// 更多的代理控制行为
devServer: {
proxy: {
// 代理1:正常写法
// 注:使用一个 path: options 成对的对象 重点
// ‘/api’前缀:可自定义
'/atguigu': {//匹配所有以’/atguigu‘开头的请求路径
target: 'http://localhost:5000', // 代理目标的基础路径
pathRewrite: {'^/atguigu':''},// 重写路径:{key'匹配所有^/atguigu : value为'空'}
ws: true, // 用于支持websocket
// 用于控制请求头中的host值
changeOrigin: true // 来自于路径:ture为5000请求服务器端口一致,false默认为8080真实端口
'/demo': {
target: 'http://localhost:5001',
// 重写路径:{key'匹配所有^/atguigu : value为'空'}
pathRewrite: {'^/demo':''},
ws: true, // 用于支持websocket
// 用于控制请求头中的host值
changeOrigin: true // 来自于路径:false为5000请求服务器端口一致,true为8080真实端口
// 代理2:精简写法
'/foo': {
target: '<other_url>'
changeOrigin设置为true时,服务器收到的请求头中的host为:localhost:5000
changeOrigin设置为false时,服务器收到的请求头中的host为:localhost:8080,
changeOrigin默认值为true
对应的App.vue完整代码
<template>
<button @click="getStudents">获取学生信息</button>
<button @click="getCars">获取汽车信息</button>
</div>
</template>
<script>
// 引入axios
import axios from 'axios'
export default {
name:'App',
methods: {
getStudents(){
// 请求get().then()返回值:前提配置好vue.config.js中代理服务器和端口
// vue.config.js中配置了/atguigu前缀,端口后放前缀
// 问题:代理转发路径时带有/atguigu报错,需重写代理路径pathRewrite
axios.get('/atguigu/http://localhost:8080/atguigu/students').then(
// 成功和失败:获取的是对象data,不是data()方法
Response => {
console.log('请求成功了!',Response.data)
error => {
console.log('请求失败了',error.message)
getCars(){
axios.get('http://localhost:8080/demo/cars').then(
response => {
console.log('请求成功了',response.data)
error => {
console.log('请求失败了',error.message)
</script>
<style>
</style>
1. 优点:可以配置多个代理,且可以灵活的控制请求是否走代理。
2. 缺点:配置略微繁琐,请求资源时必须加前缀。
github_Search案例
1、拆解index.html为Search.vue和List.vue组件
2、style中引入index.css
3、引入第三方库:bootstrap.css
1、src/assets静态资源创建Css文件夹资源:问题报错 如字体等
// 引入css问题:第三方资源字体或其他无资源时报错
// 问题解决:在公共资源public里建立静态资源Css
import './assets/css/bootstrap.css'
2、将第三方库移动至public文件夹下,并在index.html中添加 <link rel="stylesheet" href="<%= BASE_URL %>css/bootstrap.css">
<!DOCTYPE html>
<html lang="">
<meta charset="utf-8">
<!-- 针对IE浏览器的一个特殊配置,含义是让IE浏览器以最高的渲染级别渲染页面 -->
<meta http-equiv="X-UA-Compatible" content="IE=edge"
>
<!-- 开启移动端的理想视口-->
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<!-- 配置页签图标-->
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<!-- 注:引入第三方样式 -->
<link rel="stylesheet" href="<%= BASE_URL %>css/bootstrap.css">
<!-- 配置网页标题-->
<title><%= htmlWebpackPlugin.options.title %></title>
</head>
<!--当浏览器不支持js时noscript中的元素就会被渲染-->
<noscript>
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<!--容器-->
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>
完善案例完整代码:
mian.ts
//引入Vue
import Vue from 'vue'
//引入App
import App from './App.vue'
//关闭Vue的生产提示
Vue.config.productionTip = false
//创建vm
new Vue({
render: h => h(App),
beforeCreate() {//开启总线
Vue.prototype.$bus = this
}).$mount('#app')
App.vue
<template>
<div class="container">
<Search/>
<List/>
</div>
</template>
<script>
import Search from '@/components/Search'
import List from '@/components/List'
// 引入css问题:第三方资源字体或其他无资源时报错
// 问题解决:在公共资源public里建立静态资源Css
// import './assets/css/bootstrap.css'
export default {
name:'App',
components:{Search,List},
</script>
<style>
</style>
components中Search.vue
<template>
<section class="jumbotron">
<h3 class="jumbotron-heading">Search Github Users</h3>
<!--获取用户关键词:v-model='keyword'-->
<input type="text" placeholder="enter the name you search" v-model="keyWord"/>
<button @click="searchUsers">Search</button>
</div>
</section>
</template>
<script>
// 引入axios
import axios from 'axios'
export default {
name: 'Search',
data(){
return{
keyWord: '',
methods: {
searchUsers(){
//总线:提供数据,请求前更新List的数据
this.$bus.$emit('updateListData',{isLoading:true,errMsg:'',users:[],isFirst:false})
// 注意:需要模板解析要使用 ~ `` 符号包裹,q=${this.keyWord}
axios.get(`https://api.github.com/search/users?q=${this.keyWord}`).then(
response => {
console.log('请求成功了')
// 请求成功后更新List的数据
this.$bus.$emit('updateListData',{isLoading:false,errMsg:'',users:response.data.items})
error => {
// 请求后更新List的数据
this.$bus.$emit('updateListData
',{isLoading:false,errMsg:error.message,users:[]})
</script>
<style scoped>
</style>
components中List.vue
<template>
<div class="row">
<!-- 展示用户列表 -->
<!--使用获取的用户数据绑定:user.login\user.html_url\user.avatar_url属性-->
<div v-show="info.users.length" class="card" v-for="user in info.users" :key="user.login">
<a :href="user.html_url" target="_blank">
<img :src="user.avatar_url" style='width: 100px'/>
<p class="card-text">{{user.login}}</p>
</div>
<!-- 展示欢迎词 -->
<h1 v-show="info.isFirst">欢迎使用!</h1>
<!-- 展示加载中 -->
<h1 v-show="info.isLoading">加载中....</h1>
<!-- 展示错误信息 -->
<h1 v-show="info.errMsg">{{info.errMsg}}</h1>
</div>
</template>
<script>
export default {
name: 'List',
data(){
return {
info: {
isFirst: true,
isLoading: false,
errMsg:'',
users: []
// 总线:接收获取数据,使用mounted钩子
mounted () {
this.$bus.$on('updateListData',(dataObj)=>{
// info替换成dataObj:达到响应式效果
// 合并属性:{...this.info,...dataObj}以...dataObj属性为主
this.info = {...this.info,...dataObj}
</script>
<style scoped>
.album {
min-height: 50rem; /* Can be removed; just added for demo purposes */
padding-top: 3rem;
padding-bottom: 3rem;
background-color: #f7f7f7;
.card {
float: left;
width: 33.333%;
padding: .75rem;
margin-bottom: 2rem;
border: 1px solid #efefef;
text-align: center;
.card > img {
margin-bottom: .75rem;
border-radius: 100px;
.card-text {
font-size: 85%;
</style>
Vue项目中使用2个Ajax库:插件和axios的区别
mian.ts中使用插件
//引入Vue
import Vue from 'vue'
//引入App
import App from './App.vue'
//引入插件vue-resource
import vueResource from 'vue-resource'
//关闭Vue的生产提示
Vue.config.productionTip = false
// 使用插件
Vue.use(vueResource)
//创建vm
new Vue({
render: h => h(App),
beforeCreate() {//开启总线
Vue.prototype.$bus = this
}).$mount('#app')
Search.vue中:this.http.get替换axios.get
替换后,2者功能完全一致。但是推荐使用axios库方法,插件Vue.use(vueResource)不推荐
1. 作用:让父组件可以向子组件指定位置插入html结构,也是一种组件间通信的方式,适用于父组件 ===> 子组件。
2. 分类:默认插槽、具名插槽、作用域插槽
3. 使用方式:
1. 默认插槽:
app.vue父组件中:
<Category>
<div>html结构1</div>
</Category>
<!--绑定事件:listData统一事件名,便于接收-->
<!--出现列表不同样式时:不需统一事件名-->
<!--<Category title="美食" :listData="foods">
-->
<Category title="美食">
<img src="https://s3.ax1x.com/2021/01/16/srJlq0.jpg" alt="">
</Category>
<Category title="游戏">
<li v-for="(g,index) in games" :key="index">{{ g }}</li>
</Category>
<Category title="电影" :listData="films">
<!--控制播放controls:无法播放问题-->
<video controls src="http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4"></video>
</Category>
</div>
</template>
<script>
import Category from '@/components/Category'
export default {
name:'App',
components:{Category},
data(){
return{
foods:['火锅','烧烤','小龙虾','牛排'],
games:['红色警戒','穿越火线','劲舞团','超级玛丽'],
films:['《教父》','《拆弹专家》','《你好,李焕英》','《尚硅谷》']
</script>
<style lang="css">
.container{
display: flex;
/*相邻距离:调整主轴*/
justify-content: space-around;
video{
width: 100%;
</style>
Category.vue子组件中:
<template>
<!-- 定义插槽 -->
<slot>插槽默认内容...</slot>
</div>
</template>
<h3>{{ title }}分类</h3>
<!-- 定义一个插槽(挖个坑,等着组件的使用者进行填充) -->
<slot>我是一些默认值,当使用者没有传递具体结构时,我会出现</slot>
<!--<ul>
<li v-for="(item,index) in listData" :key="index">{{ item }}</li>
</ul>-->
</div>
</template>
<script>
export default {
name: 'Category',
props: ['listData',"title"]
</script>
<style scoped>
.category{
background-color: skyblue;
width: 200px;
height: 300px;
text-align: center;
background-color: orange;
img{
width: 100%;
</style>
2. 具名插槽:
App.vue中:父组件
```vue
父组件中:
<Category>
<template slot="center">
<
div>html结构1</div>
</template>
<template v-slot:footer>
<div>html结构2</div>
</template>
</Category>
<template>
<div class="container">
<Category title="美食">
<!--slot具名插槽-->
<img slot="center" src="https://s3.ax1x.com/2021/01/16/srJlq0.jpg" alt="">
<a slot="footer" href="http://www.atguigu.com" >更多美食</a>
</Category>
<Category title="游戏">
<ul slot="center">
<li v-for="(g,index) in games" :key="index">{{ g }}</li>
<div class="foot" slot="footer">
<a href="http://www.atguigu.com">单机游戏</a>
<a href="http://www.atguigu.com">网络游戏</a>
</div>
</Category>
<Category title="电影" :listData="films">
<!--控制播放controls:无法播放问题-->
<video slot="center" controls src="http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4"></video>
<!--此时template标签中的内容显示在页面上,但是看dom结构没有template标签-->
<!--template下Vue2.6插槽:v-slot才有效新写法-->
<template v-slot:footer>
<!--<template slot="footer">-->
<div class="foot">
<a href="http://www.atguigu.com">经典</a>
<a href="http://www.atguigu.com">热门</a>
<a href="http://www.atguigu.com">推荐</a>
</div>
<h4>欢迎前来观影</h4>
</template>
</Category>
</div>
</template>
<script>
import Category from '@/components/Category'
export default {
name:'App',
components:{Category},
data(){
return{
foods:['火锅','烧烤','小龙虾','牛排'],
games:['红色警戒','穿越火线','劲舞团','超级玛丽'],
films:['《教父》','《拆弹专家》','《你好,李焕英》','《尚硅谷》']
</script>
<style lang="css">
/*可以合并简写,功能相同*/
.container,.foot{
/*弹性布局*/
display: flex;
/*相邻距离:调整主轴*/
justify-content: space-around;
/*可以合并简写
*/
/*.foot{
display: flex;
!*相邻距离:调整主轴*!
justify-content: space-around;
video{
width: 100%;
text-align: center;
</style>
子组件中:
<template>
<!-- 定义插槽 -->
<slot name="center">插槽默认内容...</slot>
<slot name="footer">插槽默认内容...</slot>
</div>
</template>
Category.vue完整代码
<template>
<div class="category">
<h3>{{ title }}分类</h3>
<!-- 定义一个插槽(挖个坑,等着组件的使用者进行填充) -->
<slot name="center">我是一些默认值,当使用者没有传递具体结构时,我会出现1</slot>
<slot name="footer">我是一些默认值,当使用者没有传递具体结构时,我会出现2</slot>
</div>
</template>
<script>
export default {
name: 'Category',
props: ['listData',"title"]
</script>
<style scoped>
.category{
background-color: skyblue;
width: 200px;
height: 300px;
text-align: center;
background-color: orange;
img{
width: 100%;
</style>
3. 作用域插槽:必须使用 <template scope='自定义域名’> 标签
1. 理解:数据在组件的自身,但根据数据生成的结构需要组件的使用者来决定。(games数据在Category组件中,但使用数据所遍历出来的结构由App组件决定)
2. 具体编码:
```vue
父组件中:
<Category>
<template scope="scopeData">
<!-- 生成的是ul列表 -->
<li v-for="g in scopeData.games" :key="g">{{g}}</li>
</template>
</Category>
<Category>
<template slot-scope="scopeData">
<!-- 生成的是h4标题 -->
<h4 v-for="g in scopeData.games" :key="g">{{g}}</h4>
</template>
</Category>
子组件中:
<template>
<slot :games="games"></slot>
</div>
</template>
<script>
export default {
name:'Category',
props:['title'],
//数据在子组件自身
data() {
return {
games:['红色警戒','穿越火线','劲舞团','超级玛丽']
</script>
app.vue完整代码:
<!--作用域插槽:结构是使用者来决定,数据在组件中-->
<template>
<div class="container">
<Category title="游戏">
<!--作用域插槽:必须使用<template scope="自定义域名"标签接收data对象games-->
<template scope="atguigu">
<!--
无序列表ul-->
<li v-for="(g,index) in atguigu.games" :key="index">{{ g }}</li>
</template>
</Category>
<Category title="游戏">
<!--ES6结构赋值:scope="{games}"优化简写-->
<template scope="{games}">
<!--有序列表ol-->
<!--ES6结构赋值:scope="{games}"优化简写-->
<!--<li v-for="(g,index) in atguigu.games" :key="index">{{ g }}</li>-->
<li v-for="(g,index) in games" :key="index">{{ g }}</li>
</template>
</Category>
<Category title="游戏">
<!--新的写法-->
<template v-slot="{games}">
<!--<template scope="{games}">-->
<h4 v-for="(g,index) in games" :key="index">{{ g }}</h4>
</template>
</Category>
</div>
</template>
<script>
import Category from '@/components/Category'
export default {
name:'App',
components:{Category},
</script>
<style lang="css">
/*可以合并简写,功能相同*/
.container,.foot{
/*弹性布局*/
display: flex;
/*相邻距离:调整主轴*/
justify-content: space-around;
/*可以合并简写*/
/*.foot{
display: flex;
!*相邻距离:调整主轴*!
justify-content: space-around;
video{
width: 100%;
text-align: center;
</style>
Category.vue
<template>
<div class="category">
<h3>{{ title }}分类</h3>
<!-- 定义一个插槽(挖个坑,等着组件的使用者进行填充) -->
<!--作用域插槽:绑定事件data数据对象games-->
<!--可以传多个对象:msg-->
<slot :games="games" msg="hello">我是默认的一些内容</slot>
</div>
</template>
<script>
export default {
name: 'Category',
props: ['listData',"title"],
data(){
return{
games:['红色警戒','穿越火线','劲舞团','超级玛丽'],
</script>
<style scoped>
.category{
background-color: skyblue;
width: 200px;
height: 300px;
text-align: center;
background-color: orange;
img{
width: 100%;
</style> ```
Vuex (重点掌握)
在Vue中实现集中式状态(数据)管理的一个Vue插件,对vue应用中多个组件的共享状态进行集中式的管理(读/写),也是一种组件间通信的方式,且适用于任意组件间通信。
2.何时使用?
多个组件需要共享数据时,Vue.use(vuex)
Vue总线时:
Vuex时:
jia(context,value){
// console.log('actions中的jia被调用了',miniStore,value)
context.commit('JIA',value)
const mutations = {
//执行加
JIA(state,value){
// console.log('mutations中的JIA被调用了',state,value)
state.sum += value
//初始化数据
const state = {
sum:0
//创建并暴露store
export default new Vuex.Store({
actions,
mutations,
state,
2. 组件中读取vuex中的数据:```$store.state.sum```,模板中不用加this,
3. 组件中修改vuex中的数据:```$store.dispatch('action中的方法名',数据)``` 或 ```$store.commit('mutations中的方法名',数据)```
> 备注:若没有网络请求或其他业务逻辑,组件中也可以越过actions,即不写```dispatch```,直接编写```commit```
5.getters的使用
1. 概念:当state中的数据需要经过加工后再使用时,可以使用getters加工
2. 在```store.js```中追加```getters```配置
const getters = {
bigSum(state){
return state.sum * 10
//创建并暴露store
export default new Vuex.Store({
......
getters
3. 组件中读取数据:```$store.getters.bigSum```
6.四个map方法的使用
1. <strong>mapState方法:</strong>用于帮助我们映射```state```中的数据为计算属性
```js
computed: {
//借助mapState生成计算属性:sum、school、subject(对象写法)
...mapState({sum:'sum',school:'school',subject:'subject'}),
//借助mapState生成计算属性:sum、school、subject(数组写法)
...mapState(['sum','school','subject']),
2. <strong>mapGetters方法:</strong>用于帮助我们映射```getters```中的数据为计算属性
```js
computed: {
//借助mapGetters生成计算属性:bigSum(对象写法)
...mapGetters({bigSum:'bigSum'}),
//借助mapGetters生成计算属性:bigSum(数组写法)
...mapGetters(['bigSum'])
完整代码:index.js
//该文件用于创建Vuex中最为核心的store
import Vue from 'vue'
//引入Vuex
import Vuex from 'vuex'
//应用Vuex插件
Vue.use(Vuex)
//准备actions对象——用于响应组件中的动作
const actions = {
// jia:function (){可简写
/*jia(context,value){
// console.log('actions中的jia被调用了!',context,value)
context.commit('JIA',value)
jian(context,value){
context.commit('JIAN',value)
},*/ // 逻辑简写:功能commit直接mutations
jiaOdd(context,value){
console.log('actions中的jiaOdd被调用了!')
// 使用上下文获取this.$store.state.sum判断
// if (this.$store.state.sum % 2){
if (context.state.sum % 2){
context.commit('JIA',value)
jiaWait(context,value){
setTimeout(()=>{
context.commit('JIA',value)
},500)
//准备mutations对象——用于操作数据(state)状态
const mutations = {// 检测数据变化
JIA(state,value){
// console.log('mutations中的JIA被调用了!',state,value)
state.sum += value
JIAN (context, value) {
state.sum -= value
//准备stats对象——用于存储数据
const state = {
sum: 0, //当前的和,初始数据
School: '尚硅谷',
subject: '前端',
// 准备getters:用于将state中的数据进行加工
const getters = {
bigSum(state){
return state.sum*10
//创建Store({配置对象})
/*const store = new Vuex.Store({
actions:actions,// 同名时触发简写,如下
mutations,
stats,
暴露导出接口
export default store*/
// 进行简写优化
// 创建并暴露store
export default new Vuex.Store({
actions:actions,// 同名时触发简写,如下
mutations,
state,
getters,
Count.vue完整代码:
<template>
<!--优化:可用计算属性来省略$store.state.-->
<!--<h1>当前求和为:{{ $store.state.sum }}</h1>-->
<h1>当前求和为:{{ sum }}</h1>
<h3>当前求和为10倍:{{ bigSum }}</h3>
<h3>在{{ School }},做{{ subject }}</h3>
<!--select单选或多选菜单-->
<!--v-model.number前置转换数值型-->
<select v-model.number="n" name="" id="">
<!--v-model.number前置转换数值型::value无需绑定强制数值型-->
<!--<option :value="1">1</option>-->
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
</select>
<button @click="increment">+</button>
<button @click="decrement">-</button>
<button @click="incrementOdd">当前求和为奇数再加</button>
<button @click="incrementWait">等一等再加</button>
</template>
<script>
// 引入 {映射状态} from vuex 生成State代码
import {mapState} from 'vuex'
import {mapGetters} from 'vuex'
export default {
name: 'Count',
data(){
return{
n: 1, //用户选择的数字
computed: {
// 靠程序员自己亲自去写计算属性
/*sum(){
return this.$store.state.sum
School(){
return this.$store.state.School
subject(){
return this.$store.state.subject
},*/
// 借助mapState生成计算属性,从state中读取数据。(对象写法):效果同上
// ...mapState({})-ES6风格:逐一展开mapState中的key:value展示出来,等同
...mapState({sum:'sum',School:'School',subject:'subject'}),
// 再优化:借助mapState生成计算属性,从state中读取数据。(数组写法):效果同上
// ...mapState([value1,value2,value3]),只要kv对中的value,而且kv名要一致才可简写
...mapState(['sum','School','subject']),
/* ******************************** */
/*bigSum(){
return this.$store.getters.bigSum
},*/
// 借助mapGetters生成计算属性,从getters中读取数据。(对象写法):效果同上
// ...mapGetters({})-ES6风格:逐一展开mapGetters中的key:value展示出来,等同
...mapGetters({bigSum:'bigSum'}),
//再优化:kv名要一致,(数组写法):效果同上
...mapGetters(['bigSum']),
methods: {
increment(){
// this.sum += this.n
// 使用vuex的$store.dispatch(key,value)
// this.$store.dispatch('jia',this.n) // 逻辑简写:功能commit直接mutations
this.$store.commit('JIA',this.n)
decrement(){
// this.sum -= this.n
// this.$store.dispatch('jian',this.n) // 逻辑简写:功能commit直接mutations
this.$store.commit('JIAN',this.n)
incrementOdd(){
/*if (this.sum % 2) {
this.sum += this.n
/*if (this.$store.state.sum % 2){
this.$store.dispatch('jia',this.n)
this.$store.dispatch('jiaOdd',this.n)
incrementWait(){
/*setTimeout(()=>{
// this.sum += this.n
this.$store.dispatch('jia',this.n)
},500)*/
this.$store.dispatch('jiaWait',this.n)
mounted () {
// console.log('Count',this.$store) // 组件挂载直接输出this.$store
// 使用映射mapstate({key:value}):({sum():this.$store.state.sum})
const x = mapState({sum:'sum',School:'School',subject:'subject'})
console.log(x)
</script>
<style scoped>
button{
margin-left: 5px;
</style>
3. <strong>mapActions方法:</strong>用于帮助我们生成与```actions```对话的方法,即:包含```$store.dispatch(xxx)```的函数
Count.vue
methods:{
//靠mapActions生成:incrementOdd、incrementWait(对象形式)
...mapActions({incrementOdd:'jiaOdd',incrementWait:'jiaWait'})
//靠mapActions生成:incrementOdd、incrementWait(数组形式)
...mapActions(['jiaOdd','jiaWait'])
/*incrementOdd(){
/!*if (this.sum % 2) {
this.sum += this.n
/!*if (this.$store.state.sum % 2){
this.$store.dispatch('jia',this.n)
this.$store.dispatch('jiaOdd',this.n)
incrementWait(){
/!*setTimeout(()=>{
// this.sum += this.n
this.$store.dispatch('jia',this.n)
},500)*!/
this.$store.dispatch('jiaWait',this.n)
},*/
// 借助mapMutations生成对应的方法,方法中会调用commit去联系mutations(对象写法):效果同上
// ...mapMutations({})-ES6风格:逐一展开mapMutations中的map:key展示出来,等同
...mapActions({ incrementOdd:'jiaOdd',incrementWait:'jiaWait' }),
// 数组写法:
...mapActions(['jiaOdd','jiaWait']),
```
4. <strong>mapMutations方法:</strong>用于帮助我们生成与```mutations```对话的方法,即:包含```$store.commit(xxx)```的函数
Count.vue中
methods:{
//靠mapActions生成:increment、decrement(对象形式)
...mapMutations({increment:'JIA',decrement:'JIAN'}),
//靠mapMutations生成:JIA、JIAN(对象形式)
...mapMutations(['JIA','JIAN']),
methods: {
/*increment(){
// this.sum += this.n
// 使用vuex的$store.dispatch(key,value)
// this.$store.dispatch('jia',this.n) // 逻辑简写:功能commit直接mutations
this.$store.commit('JIA',this.n)
decrement(){
// this.sum -= this.n
// this.$store.dispatch('jian',this.n) // 逻辑简写:功能commit直接mutations
this.$store.commit('JIAN',this.n)
},*/
// 借助mapMutations生成对应的方法,方法中会调用commit去联系mutations(对象写法):效果同上
// ...mapMutations({})-ES6风格:逐一展开mapMutations中的map:key展示出来,等同
...mapMutations({increment:'JIA',decrement:'JIAN'}),
//借助mapMutations生成对应的方法,方法中会调用commit去联系mutations(数组写法)
// ...mapMutations(['JIA','JIAN']),
> 备注:mapActions与mapMutations使用时,若需要传递参数需要:在模板中绑定事件时传递好参数,否则参数是事件对象。
main.ts
//引入Vue
import Vue from 'vue'
//引入App
import App from './App.vue'
//引入插件
import vueResource from 'vue-resource'
//引入store
import store from './store'
//关闭Vue的生产提示
Vue.config.productionTip = false
//使用插件
Vue.use(vueResource)
//创建vm
new Vue({
store, // 同名触发简写store:store,
render: h => h(App),
beforeCreate() {
Vue.prototype.$bus = this
}).$mount("#app")
App.vue
<template>
<Count/>
</div>
</template>
<script>
import Count from '@/components/Count'
export default {
name:'App',
components:{Count},
mounted () {
console.log('App',this)
</script>
<style lang="css">
</style>
Store/index.js
//该文件用于创建Vuex中最为核心的store
import Vue from 'vue'
//引入Vuex
import Vuex from 'vuex'
//应用Vuex插件
Vue.use(Vuex)
//准备actions对象——用于响应组件中的动作
const actions = {
// jia:function (){可简写
/*jia(context,value){
// console.log('actions中的jia被调用了!',context,value)
context.commit('JIA',value)
jian(context,value){
context.commit('JIAN',value)
},*/ // 逻辑简写:功能commit直接mutations
jiaOdd(context,value){
console.log('actions中的jiaOdd被调用了!')
// 使用上下文获取this.$store.state.sum判断
// if (this.$store.state.sum % 2){
if (context.state.sum % 2){
context.commit('JIA',value)
jiaWait(context,value){
setTimeout(()=>{
context.commit('JIA',value)
},500)
//准备mutations对象——用于操作数据(state)状态
const mutations = {// 检测数据变化
JIA(state,value){
// console.log('mutations中的JIA被调用了!',state,value)
state.sum += value
JIAN (context, value) {
state.sum -= value
//准备stats对象——用于存储数据
const state = {
sum: 0, //当前的和,初始数据
School: '尚硅谷',
subject: '前端',
// 准备getters:用于将state中的数据进行加工
const getters = {
bigSum(state){
return state.sum*10
//创建Store({配置对象})
/*const store = new Vuex.Store({
actions:actions,// 同名时触发简写,如下
mutations,
stats,
暴露导出接口
export default store*/
// 进行简写优化
// 创建并暴露store
export default new Vuex.Store({
actions:actions,// 同名时触发简写,如下
mutations,
state,
getters,
components/Count.vue
<template>
<!--优化:可用计算属性来省略$store.state.-->
<!--<h1>当前求和为:{{ $store.state.sum }}</h1>-->
<h1>当前求和为:{{ sum }}</h1>
<h3>当前求和为10倍:{{ bigSum }}</h3>
<h3>在{{ School }},做{{ subject }}</h3>
<!--select单选或多选菜单-->
<!--v-model.number前置转换数值型-->
<select v-model.number="n" name="" id="">
<!--v-model.number前置转换数值型::value无需绑定强制数值型-->
<!--<option :value="1">1</option>-->
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
</select>
<button @click="increment(n)">+</button>
<button @click="decrement(n)">-</button>
<button @click="incrementOdd(n)">当前求和为奇数再加</button>
<button @click="incrementWait(n)">等一等再加</button>
</div>
</template>
<script>
// 引入 {映射状态} from vuex 生成State代码
import {mapState,mapGetters,mapMutations,mapActions} from 'vuex'
// import {mapGetters} from 'vuex'
// import { mapMutations } from 'vuex'
export default {
name: 'Count',
data(){
return{
n: 1, //用户选择的数字
computed: {
// 借助mapState生成计算属性,从state中读取数据。(对象写法):效果同上
// ...mapState({})-ES6风格:逐一展开mapState中的key:value展示出来,等同
...mapState({sum:'sum',School:'School'
,subject:'subject'}),
// 再优化:借助mapState生成计算属性,从state中读取数据。(数组写法):效果同上
// ...mapState([value1,value2,value3]),只要kv对中的value,而且kv名要一致才可简写
...mapState(['sum','School','subject']),
/* ******************************** */
// 借助mapGetters生成计算属性,从getters中读取数据。(对象写法):效果同上
// ...mapGetters({})-ES6风格:逐一展开mapGetters中的key:value展示出来,等同
...mapGetters({bigSum:'bigSum'}),
//再优化:kv名要一致,(数组写法):效果同上
...mapGetters(['bigSum']),
methods: {
/*increment(){
// this.sum += this.n
// 使用vuex的$store.dispatch(key,value)
// this.$store.dispatch('jia',this.n) // 逻辑简写:功能commit直接mutations
this.$store.commit('JIA',this.n)
decrement(){
// this.sum -= this.n
// this.$store.dispatch('jian',this.n) // 逻辑简写:功能commit直接mutations
this.$store.commit('JIAN',this.n)
},*/
// 借助mapMutations生成对应的方法,方法中会调用commit去联系mutations(对象写法):效果同上
// ...mapMutations({})-ES6风格:逐一展开mapMutations中的map:key展示出来,等同
...mapMutations({increment:'JIA',decrement:'JIAN'}),
//借助mapMutations生成对应的方法,方法中会调用commit去联系mutations(数组写法)
// ...mapMutations(['JIA','JIAN']),
/* **************************** */
/*incrementOdd(){
/!*if (this.sum % 2) {
this.sum += this.n
/!*if (this.$store.state.sum % 2){
this.$store.dispatch('jia',this.n)
this.$store.dispatch('jiaOdd',this.n)
incrementWait(){
/!*setTimeout(()=>{
// this.sum += this.n
this.$store.dispatch('jia',this.n)
},500)*!/
this.$store.dispatch('jiaWait',this.n)
},*/
// 借助mapMutations生成对应的方法,方法中会调用commit去联系mutations(对象写法):效果同上
// ...mapMutations({})-ES6风格:逐一展开mapMutations中的map:key展示出来,等同
...mapActions({ incrementOdd:'jiaOdd',incrementWait:'jiaWait' }),
// 数组写法:
...mapActions(['jiaOdd','jiaWait']),
mounted () {
// console.log('Count',this.$store) // 组件挂载直接输出this.$store
// 使用映射mapState({key:value}):({sum():this.$store.state.sum})
const x = mapState({sum:'sum',School:'School',subject:'subject'})
console.log(x)
</script>
<style scoped>
button{
margin-left: 5px;
</style>
多组件共享数据
App.vue中新建Person组件
Person.vue组件代码:
<template>
<h1>人员列表</h1>
<h3 style="color:red">Count组件的求和为:{{ sum }}</h3>
<input type="text" placeholder="请输入名字" v-model="name">
<button @click="add">添加</button>
<!--<li v-for="p in $store.state.personalbar" key="p.id">{{ p.name }}</li>-->
<!--使用computed计算属性:优化$store.state.-->
<li v-for="p in personList" :key="p.id">{{ p.name }}</li>
</div>
</template>
<script>
import {nanoid} from 'nanoid'
export default {
name: 'Person',
data() {
return {
name:''
computed:{
personList(){
return this.$store.state.personList
sum(){
return this.$store.state.sum
// 简写可避免问题:
// ...mapState(['personList']),
methods:{
add(){
// 将输入的名字包装成对象
const personObj
= {id:nanoid(),name:this.name}
// console.log(personObj)
this.$store.commit('ADD_PERSON',personObj)
// 保持输入框为空
this.name= ''
</script>
<style scoped>
</style>
Count.vue组件代码:
<template>
<!--优化:可用计算属性来省略$store.state.-->
<!--<h1>当前求和为:{{ $store.state.sum }}</h1>-->
<h1>当前求和为:{{ sum }}</h1>
<h3>当前求和为10倍:{{ bigSum }}</h3>
<h3>在{{ School }},做{{ subject }}</h3>
<h3 style="color:red">Person组件的总人数是:{{ personList.length }}</h3>
<!--select单选或多选菜单-->
<!--v-model.number前置转换数值型-->
<select v-model.number="n" name="" id="">
<!--v-model.number前置转换数值型::value无需绑定强制数值型-->
<!--<option :value="1">1</option>-->
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
</select>
<button @click="increment(n)">+</button>
<button @click="decrement(n)">-</button>
<button @click="incrementOdd(n)">当前求和为奇数再加</button>
<button @click="incrementWait(n)">等一等再加</button>
</div>
</template>
<script>
// 引入 {映射状态} from vuex 生成State代码
import {mapState,mapGetters,mapMutations,mapActions} from 'vuex'
// import {mapGetters} from 'vuex'
// import { mapMutations } from 'vuex'
export default {
name: 'Count',
data(){
return{
n: 1, //用户选择的数字
computed: {
// 借助mapState生成计算属性,从state中读取数据。(对象写法):效果同上
// ...mapState({})-ES6风格:逐一展开mapState中的key:value展示出来,等同
...mapState({sum:'sum',School:'School',subject:'subject'}),
// 再优化:借助mapState生成计算属性,从state中读取数据。(数组写法):效果同上
// ...mapState([value1,value2,value3]),只要kv对中的value,而且kv名要一致才可简写
...mapState(['sum','School','subject',"personList"]),
/* ******************************** */
// 借助mapGetters生成计算属性,从getters中读取数据。(对象写法):效果同上
// ...mapGetters({})-ES6风格:逐一展开mapGetters中的key:value展示出来,等同
...mapGetters({bigSum:'bigSum'}),
//再优化:kv名要一致,(数组写法):效果同上
...mapGetters(['bigSum']),
methods: {
/*increment(){
// this.sum += this.n
// 使用vuex的$store.dispatch(key,value)
// this.$store.dispatch('jia',this.n) // 逻辑简写:功能commit直接mutations
this.$store.commit('JIA',this.n)
decrement(){
// this.sum -= this.n
// this.$store.dispatch('jian',this.n) // 逻辑简写:功能commit直接mutations
this.$store.commit('JIAN',this.n)
},*/
// 借助mapMutations生成对应的方法,方法中会调用commit去联系mutations(对象写法):效果同上
// ...mapMutations({})-ES6风格:逐一展开mapMutations中的map:key展示出来,等同
...mapMutations({increment:'JIA',decrement:'JIAN
'}),
//借助mapMutations生成对应的方法,方法中会调用commit去联系mutations(数组写法)
// ...mapMutations(['JIA','JIAN']),
/* **************************** */
// 借助mapMutations生成对应的方法,方法中会调用commit去联系mutations(对象写法):效果同上
// ...mapMutations({})-ES6风格:逐一展开mapMutations中的map:key展示出来,等同
...mapActions({ incrementOdd:'jiaOdd',incrementWait:'jiaWait' }),
// 数组写法:
...mapActions(['jiaOdd','jiaWait']),
mounted () {
// console.log('Count',this.$store) // 组件挂载直接输出this.$store
// 使用映射mapState({key:value}):({sum():this.$store.state.sum})
const x = mapState({sum:'sum',School:'School',subject:'subject'})
console.log(x)
</script>
<style scoped>
button{
margin-left: 5px;
</style>
index.js或Vuex.js核心store代码:
//该文件用于创建Vuex中最为核心的store
import Vue from 'vue'
//引入Vuex
import Vuex from 'vuex'
//应用Vuex插件
Vue.use(Vuex)
//准备actions对象——用于响应组件中的动作
const actions = {
// jia:function (){可简写
/*jia(context,value){
// console.log('actions中的jia被调用了!',context,value)
context.commit('JIA',value)
jian(context,value){
context.commit('JIAN',value)
},*/ // 逻辑简写:功能commit直接mutations
jiaOdd(context,value){
console.log('actions中的jiaOdd被调用了!')
// 使用上下文获取this.$store.state.sum判断
// if (this.$store.state.sum % 2){
if (context.state.sum % 2){
context.commit('JIA',value)
jiaWait(context,value){
setTimeout(()=>{
context.commit('JIA',value)
},500)
//准备mutations对象——用于操作数据(state)状态
const mutations = {// 检测数据变化
JIA(state,value){
// console.log('mutations中的JIA被调用了!',state,value)
state.sum += value
JIAN (context, value) {
state.sum -= value
// 添加人员:无需判断限制,可直接Mutations
ADD_PERSON (state,value) {
console.log('mutations中的ADD_PERSON被调用了!')
// unshift(value):从数组最前面添加元素
state.personList.unshift(value)
//准备stats对象——用于存储数据
const state = {
sum: 0, //当前的和,初始数据
School: '尚硅谷',
subject: '前端',
personList: [
{id:'001',name:'模拟'}
// 准备getters:用于将state中的数据进行加工
const getters = {
bigSum(state){
return state.sum*10
//创建Store({配置对象})
/*const store = new Vuex.Store({
actions:actions,// 同名时触发简写,如下
mutations,
stats,
暴露导出接口
export default store*/
// 进行简写优化
// 创建并暴露store
export default new Vuex.Store({
actions:actions,// 同名时触发简写,如下
mutations,
state,
getters,
7.模块化+命名空间:优化Vuex中store
this.$store.state.personAbout.list
//方式二:借助mapState读取:
...mapState('countAbout',['sum','school','subject']),
4. 开启命名空间后,组件中读取getters数据:
```js
//方式一:自己直接读取
this.$store.getters['personAbout/firstPersonName']
//方式二:借助mapGetters读取:
...mapGetters('countAbout',['bigSum'])
5. 开启命名空间后,组件中调用dispatch
```js
//方式一:自己直接dispatch
this.$store.dispatch('personAbout/addPersonWang',person)
//方式二:借助mapActions:
...mapActions('countAbout',{incrementOdd:'jiaOdd',incrementWait:'jiaWait'})
6. 开启命名空间后,组件中调用commit
```js
//方式一:自己直接commit
this.$store.commit('personAbout/ADD_PERSON',person)
//方式二:借助mapMutations:
...mapMutations('countAbout',{increment:'JIA',decrement:'JIAN'}),
main.ts完整代码
//引入Vue
import Vue from 'vue'
//引入App
import App from './App.vue'
//引入插件
import vueResource from 'vue-resource'
//引入store
import store from './store'
//关闭Vue的生产提示
Vue.config.productionTip = false
//使用插件
Vue.use(vueResource)
//创建vm
new Vue({
store, // 同名触发简写store:store,
render: h => h(App),
beforeCreate() {
Vue.prototype.$bus = this
}).$mount("#app")
App.vue完整代码
<template>
<Count/>
<Person/>
</div>
</template>
<script>
import Count from '@/components/Count'
import Person from '@/components/Person'
export default {
name:'App',
components:{Count,Person},
mounted () {
console.log('App',this)
</script>
<style lang="css">
</style>
Count.vue完整代码:使用vue模块化:import {mapState,mapMutations,mapActions,mapGetters} from 'vuex'
<template>
<!--优化:可用计算属性来省略$store.state.-->
<!--
<h1>当前求和为:{{ $store.state.sum }}</h1>-->
<h1>当前求和为:{{ sum }}</h1>
<h3>当前求和为10倍:{{ bigSum }}</h3>
<h3>在{{ School }},做{{ subject }}</h3>
<h3 style="color:red">Person组件的总人数是:{{ personList.length }}</h3>
<!--select单选或多选菜单-->
<!--v-model.number前置转换数值型-->
<select v-model.number="n" name="" id="">
<!--v-model.number前置转换数值型::value无需绑定强制数值型-->
<!--<option :value="1">1</option>-->
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
</select>
<button @click="increment(n)">+</button>
<button @click="decrement(n)">-</button>
<button @click="incrementOdd(n)">当前求和为奇数再加</button>
<button @click="incrementWait(n)">等一等再加</button>
</div>
</template>
<script>
// 引入 {映射状态} from vuex 生成State代码
import {mapState,mapGetters,mapMutations,mapActions} from 'vuex'
// import {mapGetters} from 'vuex'
// import { mapMutations } from 'vuex'
export default {
name: 'Count',
data(){
return{
n: 1, //用户选择的数字
computed: {
/*// 借助mapState生成计算属性,从state中读取数据。(对象写法):效果同上
// ...mapState({})-ES6风格:逐一展开mapState中的key:value展示出来,等同
...mapState({sum:'sum',School:'School',subject:'subject'}),
// 再优化:借助mapState生成计算属性,从state中读取数据。(数组写法):效果同上
// ...mapState([value1,value2,value3]),只要kv对中的value,而且kv名要一致才可简写
...mapState(['sum','School','subject',"personList"]),*/
//使用模块化Vuex: Store中的 a,b 模块
// 模块化Vuex:命名空间必须开启,否则无法使用
...mapState('countAbout',['sum','School','subject']),
...mapState('personAbout',['personList']),
/* ******************************** */
// 借助mapGetters生成计算属性,从getters中读取数据。(对象写法):效果同上
// ...mapGetters({})-ES6风格:逐一展开mapGetters中的key:value展示出来,等同
// ...mapGetters({bigSum:'bigSum'}),
// 模块化Vuex:
...mapGetters('countAbout',['bigSum']),
/*//再优化:kv名要一致,(数组写法):效果同上
...mapGetters(['bigSum']),*/
methods: {
/*increment(){
// this.sum += this.n
// 使用vuex的$store.dispatch(key,value)
// this.$store.dispatch('jia',this.n) // 逻辑简写:功能commit直接mutations
this.$store.commit('JIA',this.n)
decrement(){
// this.sum -= this.n
// this.$store.dispatch('jian',this.n) // 逻辑简写:功能commit直接mutations
this.$store.commit('JIAN',this.n)
},*/
// 借助mapMutations生成对应的方法,方法中会调用commit去联系mutations(对象写法):效果同上
// ...mapMutations({})-ES6风格:逐一展开mapMutations中的map:key展示出来,等同
// ...mapMutations({increment:'JIA',decrement:'JIAN'}),
// 使用模块化Vuex
...mapMutations('countAbout',{increment:'JIA',decrement:'JIAN'}),
//借助mapMutations生成对应的方法,方法中会调用commit去联系mutations(数组写法)
// ...mapMutations(['JIA','JIAN']),
/* **************************** */
// 借助mapMutations生成对应的方法,方法中会调用commit去联系mutations(对象写法):效果同上
// ...mapMutations({})-ES6风格:逐一展开mapMutations中的map:key展示出来,等同
// ...mapActions({ incrementOdd:'jiaOdd',incrementWait:'jiaWait' }),
// 使用Vuex模块化:
...mapActions('countAbout',{incrementOdd:'jiaOdd',incrementWait:'jiaWait'})
// 数组写法:
// ...mapActions(['jiaOdd','jiaWait']),
mounted () {
// console.log('Count',this.$store) // 组件挂载直接输出this.$store
// 使用映射mapState({key:value}):({sum():this.$store.state.sum})
// const x = mapState({sum:'sum',School:'School',subject:'subject'})
console.log(this.$store)
</script>
<style scoped>
button{
margin-left: 5px;
</style>
Person.vue完整代码:使用Vuex模块化:未使用。。。mapState,mapActions,mapMutions,mapGetters
<template>
<h1>人员列表</h1>
<h3 style="color:red">Count组件的求和为:{{ sum }}</h3>
<h3>列表中第一个人的名字是:{{firstPersonName}}</h3>
<input type="text" placeholder="请输入名字" v-model="name">
<button @click="add">添加</button>
<button @click="addWang">添加一个姓王的人</button>
<button @click="addPersonServer">添加一个人,名字随机</button>
<!--<li v-for="p in $store.state.personalbar" key="p.id">{{ p.name }}</li>-->
<!--使用computed计算属性:优化$store.state.-->
<li v-for="p in personList" :key="p.id">{{ p.name }}</li>
</div>
</template>
<script>
import {nanoid} from 'nanoid'
export default {
name: 'Person',
data() {
return {
name:''
computed:{
personList(){
return this.$store.state.personAbout.personList
sum(){
// return this.$store.state.sum
// 使用Vuex模块化
return this.$store.state.countAbout.sum
// 简写可避免问题:
// ...mapState(['personList']),
firstPersonName(){
return this.$store.getters['personAbout/firstPersonName']
methods:{
add(){
// 将输入的名字包装成对象
const personObj = {id:nanoid(),name:this.name}
// console.log(personObj)
// this.$store.commit('ADD_PERSON',personObj)
// 模块化vuex: '分类名/ADD_PERSON‘ 提交写法
this.$store.commit('personAbout/ADD_PERSON',personObj)
// 保持输入框为空
this.name= ''
addWang(){
const personObj={id:nanoid(),name:this.name}
this.$store.dispatch('personAbout/addPersonWang',personObj)
this.name=''
addPersonServer(){
this.$store.dispatch('personAbout/addPersonServer')
</script>
<style scoped>
</style>
Store中index.js完整代码
//该文件用于创建Vuex中最为核心的store
import Vue from 'vue'
//引入Vuex
import Vuex from 'vuex'
// 引入Vuex模块
import countOptions from './count'
import personOptions from './person'
//应用Vuex插件
Vue.use(Vuex)
// 进行Vuex模块化:优化
// 求和功能相关的配置
// 模块优化:将代码迁移至count.js中
/*const countOptions = {
namespaced: true, // 命名空间开启:可简写
actions:{
jiaOdd(context,value){
console.log('actions中的jiaOdd被调用了!')
// 使用上下文获取this.$store.state.sum判断
// if (this.$store.state.sum % 2){
if (context.state.sum % 2){
context.commit('JIA',value)
jiaWait(context,value){
setTimeout(()=>{
context.commit('JIA',value)
},500)
mutations:{
JIA(state,value){
// console.log('mutations中的JIA被调用了!',state,value)
state.sum += value
JIAN (state, value) {
state.sum -= value
state:{
sum: 0, //当前的和,初始数据
School: '尚硅谷',
subject: '前端',
getters:{
bigSum(state){
return state.sum*10
//
人员管理功能相关的配置
// 模块优化:将代码迁移至person.js中
/*const personOptions = {
namespaced:true, // 命名空间开启:可简写
actions:{},
mutations:{
// 添加人员:无需判断限制,可直接Mutations
ADD_PERSON (state,value) {
console.log('mutations中的ADD_PERSON被调用了!')
// unshift(value):从数组最前面添加元素
state.personList.unshift(value)
state:{
personList: [
{id:'001',name:'模拟'}
getters:{
bigSum(state){
return state.sum*10
// 使用Vuex模块化:优化
//准备actions对象——用于响应组件中的动作
/*const actions = {
// jia:function (){可简写
/!*jia(context,value){
// console.log('actions中的jia被调用了!',context,value)
context.commit('JIA',value)
jian(context,value){
context.commit('JIAN',value)
},*!/ // 逻辑简写:功能commit直接mutations
jiaOdd(context,value){
console.log('actions中的jiaOdd被调用了!')
// 使用上下文获取this.$store.state.sum判断
// if (this.$store.state.sum % 2){
if (context.state.sum % 2){
context.commit('JIA',value)
jiaWait(context,value){
setTimeout(()=>{
context.commit('JIA',value)
},500)
//准备mutations对象——用于操作数据(state)状态
const mutations = {// 检测数据变化
JIA(state,value){
// console.log('mutations中的JIA被调用了!',state,value)
state.sum += value
JIAN (context, value) {
state.sum -= value
// 添加人员:无需判断限制,可直接Mutations
ADD_PERSON (state,value) {
console.log('mutations中的ADD_PERSON被调用了!')
// unshift(value):从数组最前面添加元素
state.personList.unshift(value)
//准备stats对象——用于存储数据
const state = {
sum: 0, //当前的和,初始数据
School: '尚硅谷',
subject: '前端',
personList: [
{id:'001',name:'模拟'}
// 准备getters:用于将state中的数据进行加工
const getters = {
bigSum(state){
return state.sum*10
//创建Store({配置对象})
/*const store = new Vuex.Store({
actions:actions,// 同名时触发简写,如下
mutations,
stats,
暴露导出接口
export default store*/
// 进行简写优化
// 创建并暴露store
export default new Vuex.Store({
/*actions:actions,// 同名时触发简写,如下
mutations,
state,
getters,*/
// Vuex模块化:命名空间必须开启
modules:{
countAbout:countOptions,
personAbout:personOptions,
Count.js完整代码
// 进行Vuex模块化:优化
// 求和功能相关的配置
// 优化:将代码index.js迁移至count.js中
export default {
namespaced: true, // 命名空间开启:可简写
actions:{
jiaOdd(context,value){
console.log('actions中的jiaOdd被调用了!')
// 使用上下文获取this.$store.state.sum判断
// if (this.$store.state.sum % 2){
if (context.state.sum % 2){
context.commit('JIA',value)
jiaWait(context,value){
setTimeout(()=>{
context.commit('JIA',value)
},500)
mutations:{
JIA(state,value){
// console.log('mutations中的JIA被调用了!',state,value)
state.sum += value
JIAN (state, value) {
state.sum -= value
state:{
sum: 0, //当前的和,初始数据
School: '尚硅谷',
subject: '前端',
getters:{
bigSum(state){
return state.sum*10
Person.js完整代码
// 人员管理功能相关的配置
// 模块优化:将代码迁移至person.js中
import axios from 'axios'
import { nanoid } from 'nanoid'
export default {
namespaced:true, // 命名空间开启:可简写
actions:{
addPersonWang(context,value){
//判断名字value值中含’王‘
if (value.name.indexOf('王')=== 0){
context.commit('ADD_PERSON',value)
}else{
alert('添加的人必须姓王')
// 从服务要一个名字:需要引入axios
addPersonServer (context) {
// 发起get请求:成功和失败的回调
axios.get('https://api。uixsj.cn/hitokoto/get?type=social').then(
Response => {
context.commit('ADD_PERSON',{id:nanoid(),name:Response.data})
error => {
alert(error.message)
mutations:{
// 添加人员:无需判断限制,可直接Mutations
ADD_PERSON (state,value) {
console.log('mutations中的ADD_PERSON被调用了!')
// unshift(value):从数组最前面添加元素
state.personList.unshift(value)
state:{
personList: [
{id:'001',name:'模拟'}
getters:{
bigSum(state){
return state.sum*10
路由:Vue中重点(重点的重点)
vue-router的理解:vue 的一个插件库,专门用来实现 SPA 应用
对 SPA 应用的理解:
1. 单页 Web 应用(single page web application,SPA)。
2. 整个应用只有一个完整的页面。
3. 点击页面中的导航链接不会刷新页面,只会做页面的局部更新。
4. 数据需要通过 ajax 请求获取。
1. 理解: 一个路由(route)就是一组映射关系(key - value),多个路由需要路由器(router)进行管理。
2. 前端路由:key是路径,value是组件。
key 为路径, value 可能是 function 或 component。前端路由component和后端路由function
1. 后端路由:
1) 理解:value 是 function, 用于处理客户端提交的请求。
2) 工作过程:服务器接收到一个请求时, 根据请求路径找到匹配的函数来处理请求, 返回响应数据。
2. 前端路由:
1) 理解:value 是 component,用于展示页面内容。
2) 工作过程:当浏览器的路径改变时, 对应的组件就会显示。
1.基本使用
1. 安装vue-router,命令:
npm i vue-router
2. 应用插件:
Vue.use(VueRouter)
3. 编写router配置项:
```js
//引入VueRouter
import VueRouter from 'vue-router'
//引入Luyou 组件
import About from '../components/About'
import Home from '../components/Home'
//创建router实例对象,去管理一组一组的路由规则
const router = new VueRouter({
routes:[
path:'/about',
component:About
path:'/home',
component:Home
//暴露router
export default router
router/index.ts完整代码
import Vue from 'vue' //注:这句必须要有,虽然在main.js里面已经引入过Vue,但是这里不要这句的话,就直接报错了Vue is not defined
// 该文件专门用于创建整个应用的路由器
import VueRouter, { RouteConfig } from 'vue-router'
// 引入组件
import About from '../components/About.vue'
import Home from '@/components/Home.vue'
Vue.use(VueRouter)
// 创建并暴露一个路由器
export default new VueRouter({
routes:[//注:此处的方法名,记住这里是routes,不是routers,没有r,要是写成routers,控制台不会报错,就是渲染不出组件来,牢记啊!不然会让人崩溃的
path:'/about',
component: About,//注:此处容易跟着代码提示一不小心写成components,要注意,控制台报错TypeError: Cannot read property '$createElement' of undefined
}, // 一组路由KV对
path: '/Home',
component: Home,
}, // 一组路由KV对
], // 数组管理一堆route路由
/*默认路由数据
import HomeView from '../views/HomeView.vue'
Vue.use(VueRouter)
const routes: Array<RouteConfig> = [
path: '/',
name: 'home',
component: HomeView
path: '/about',
name: 'about',
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import(/!* webpackChunkName: "about" *!/ '../views/AboutView.vue')
const router = new VueRouter({
mode: 'history',
base: process.env.BASE_URL,
routes
export default router
4. 实现切换(active-class可配置高亮样式)
App.vue
<router-link active-class="active" to="/about">About</router-link>
5. 指定展示位置
App.vue
<router-view></router-view>
2.几个注意点
1. 路由组件通常存放在```pages```文件夹,一般组件通常存放在```components```文件夹,一般组件就是自己写组件标签 <Banner/>。
2. 通过切换,“隐藏”了的路由组件,默认是被销毁掉的,需要的时候再去挂载。
About.vue和Home.vue中
3. 每个组件都有自己的```$route```属性,里面存储着自己的路由信息。
4. 整个应用只有一个router,可以通过组件的```$router```属性获取到。
3.嵌套多级路由(多级路由)
解析静态模板:home-message.html和home.news
将静态模板拆分为Home.vue中的子组件message.vue和news.vue组件
1. 配置路由规则,使用children配置项:
index.js中
routes:[
path:'/about',
component:About,
path:'/home',
component:Home,
children:[ //通过children配置子级路由
path:'news', //此处一定不要写:/news
component:News
path:'message',//此处一定不要写:/message
component:Message
2、路由器中router/index.ts,将拆分的message.vue和news.vue子组件写入一级路由Home:
import Vue from 'vue' //注:这句必须要有,虽然在main.js里面已经引入过Vue,但是这里不要这句的话,就直接报错了Vue is not defined
// 该文件专门用于创建整个应用的路由器
import VueRouter, { RouteConfig } from 'vue-router'
// 引入组件
import About from '@/pages/About.vue'
import Home from '@/pages/Home.vue'
import News from '@/pages/News.vue'
import Message from '../pages/Message.vue'
Vue.use(VueRouter)
// 创建并暴露一个路由器
export default new VueRouter({
routes:[//注:此处的方法名,记住这里是routes,不是routers,没有r,要是写成routers,控制台不会报错,就是渲染不出组件来,牢记啊!不然会让人崩溃的
{// 一级路由
path:'/about',
component: About,//注:此处容易跟着代码提示一不小心写成components,要注意,控制台报错TypeError: Cannot read property '$createElement' of undefined
}, // 一组路由KV对
{// 一级路由
path: '/Home',
component: Home,
// 子路由使用数组:一级路由下二级路由
children: [ // 子路由:可能N多个,所以使用数组
path: 'news', // 子路由:默认有‘/',遍历children时自动增加’/'
component: News,
path: 'message',
component: Message,
}, // 一组路由KV对
], // 数组管理一堆route路由
/*源router内容
import HomeView from '../views/HomeView.vue'
Vue.use(VueRouter)
const routes: Array<RouteConfig> = [
path: '/',
name: 'home',
component: HomeView
path: '/about',
name: 'about',
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import(/!* webpackChunkName: "about" *!/ '../views/AboutView.vue')
const router = new VueRouter({
mode: 'history',
base: process.env.BASE_URL,
routes
export default router
2. 跳转(要写完整路径):
Home.vue中
<router-link to="/home/news">News</router-link>
Home.vue完整代码:
<template>
<h2>Home组件内容</h2>
<ul class="nav nav-tabs">
<!--<a class="list-group-item active" href="./home-news.html">News</a>-->
<!--将a标签转变成router路由标签:router-link,to='完整跳转路径‘-->
<!--注:子路由to='完整路径名’-->
<router-link class="list-group-item" active-class="active" to="/home/news">News</router-link>
<!--<a class="list-group-item " href="./home-message.html">Message</a>-->
<router-link class="list-group-item" active-class="active" to="/home/message">Message</router-link>
<!--编写路由匹配规则:router/index.vue-->
<router-view></router-view>
</div>
</div>
</template>
<script>
export default {
name: 'Home',
/*beforeDestroy () {// 周期钩子:销毁
console.log('Home组件即将被销毁了')
mounted () {// 挂载完毕
console.log("Home挂载完毕",this)
},*/
</script>
<style scoped>
</style>
4.路由的query参数
1. 传递参数
Message.vue
<!-- 跳转并携带query参数,to的字符串写法 -->
<router-link :to="/home/message/detail?id=666&title=你好">跳转</router-link>
<!-- 跳转并携带query参数,to的对象写法 -->
<router-link
:to="{
path:'/home/message/detail',
query:{
id:666,
title:'你好'
>跳转</router-link>
Message.vue完整代码:
<template>
<!--v-for 必须配 :key-->
<li v-for="m in messageList" :key="m.id">
<!--<a href="/message1">{{ m.title }}</a> -->
<!--使用router路由:to='完整路由'-->
<!--<router-link to="/home/message/detail">{{ m.title }}</router-link> -->
<!--query传参:to='完整路由?id=666&title=你好啊!'-->
<!--<router-link to="/home/message/detail?id=666&title=你好啊!">{{ m.title }}</router-link> -->
<!--跳转路由并携带query参数,to的字符串写法,
带有js参数${m.id}的:解析需要绑定:to="``",注意:``-->
<!--<router-link :to="`/home/message/detail?id=${m.id}&title=${m.title}`">{{ m.title }}</router-link> -->
<!-- 跳转路由并携带query参数,to的对象写法(推荐) -->
<router-link :to="{
// 编写2个属性
path: '/home/message/detail', // 路由路径
query: { // query对象传参
id: m.id,
title: m.title,
{{ m.title }}
</router-link>
<router-view></router-view>
</div>
</template>
<script>
export default {
name: 'Message',
data(){
return{
messageList:[
// 可以使用axios从服务器获取数据
{id:'001',title:'消息001'},
{id:'002',title:'消息002'},
{id:'003',title:'消息003'},
</script>
<style scoped>
</style>
2. 三级路由组件Detail.vue中接收参数:
Detail.vue
$route.query.id
$route.query.title
完整代码:
<!--编写路由信息:router/index.ts-->
<!--编写路由信息时:二级路由Message下:三级路由Detail-->
<template>
<li>消息编号:{{ $route.query.id }}</li>
<li>消息标题:{{ $route.query.title }}</li>
</template>
<script>
export default {
name: 'Detail',
mounted(){
// console.log(this.$route) // 获取当前路由信息
</script>
<style scoped>
</style>
5.命名路由
1. 作用:可以简化路由的跳转。
2. 如何使用
1. 给路由命名:
router/index.ts完整代码
import Vue from 'vue' //注:这句必须要有,虽然在main.js里面已经引入过Vue,但是这里不要这句的话,就直接报错了Vue is not defined
// 该文件专门用于创建整个应用的路由器
import VueRouter, { RouteConfig } from 'vue-router'
// 引入组件
import About from '@/pages/About.vue'
import Home from '@/pages/Home.vue'
import News from '@/pages/News.vue'
import Message from '../pages/Message.vue'
import Detail from '@/pages/Detail.vue'
Vue.use(VueRouter)
// 创建并暴露一个路由器
export default new VueRouter({
routes:[//注:此处的方法名,记住这里是routes,不是routers,没有r,要是写成routers,控制台不会报错,就是渲染不出组件来,牢记啊!不然会让人崩溃的
{// 一级路由
name: 'guanyu', // 命名路由:简化跳转编码
path:'/about',
component: About,//注:此处容易跟着代码提示一不小心写成components,要注意,控制台报错TypeError: Cannot read property '$createElement' of undefined
}, // 一组路由KV对
{// 一级路由
path: '/Home',
component: Home,
// 子组件二级路由数组:一级路由下二级路由
children: [ // 子路由:可能N多个,所以使用数组
path: 'news', // 子路由:默认有‘/',遍历children时自动增加’/'
component: News,
path: 'message',
component: Message,
children:[//三级路由:message子组件的子组件Detail
name:'xiangqing', // 命名路由:简化跳转编码
path: 'detail',
component: Detail,
}, // 一组路由KV对
], // 数组管理一堆route路由
2. 简化跳转:message.vue
<!--v-for 必须配 :key-->
<li v-for="m in messageList" :key="m.id">
<!--<a href="/message1">{{ m.title }}</a> -->
<!--使用router路由:to='完整路由'-->
<!--<router-link to="/home/message/detail">{{ m.title }}</router-link> -->
<!--query传参:to='完整路由?id=666&title=你好啊!'-->
<!--<router-link to="/home/message/detail?id=666&title=你好啊!">{{ m.title }}</router-link> -->
<!--跳转路由并携带query参数,to的字符串写法,
带有js参数${m.id}的:解析需要绑定:to="``",注意:``-->
<!--<router-link :to="`/home/message/detail?id=${m.id}&title=${m.title}`">{{ m.title }}</router-link> -->
<!-- 跳转路由并携带query参数,to的对象写法(推荐) -->
<router-link :to="{
// 编写2个属性
// path: '/home/message/detail', // 路由路径
name: 'xiangqing', // // 命名路由index.ts中:简化path:跳转编码效果同上
query: { // query对象传参
id: m.id,
title: m.title,
{{ m.title }}
</router-link>
<router-view></router-view>
</div>
</template>
<script>
export default {
name: 'Message',
data(){
return{
messageList:[
// 可以使用axios从服务器获取数据
{id:'001',title:'消息001'},
{id:'002',title:'消息002'},
{id:'003',title:'消息003'},
</script>
<style scoped>
</style>
6.路由的params参数
1. 配置路由,声明接收params参数
router/index.ts中
import Vue from 'vue' //注:这句必须要有,虽然在main.js里面已经引入过Vue,但是这里不要这句的话,就直接报错了Vue is not defined
// 该文件专门用于创建整个应用的路由器
import VueRouter, { RouteConfig } from 'vue-router'
// 引入组件
import About from '@/pages/About.vue'
import Home from '@/pages/Home.vue'
import News from '@/pages/News.vue'
import Message from '../pages/Message.vue'
import Detail from '@/pages/Detail.vue'
Vue.use(VueRouter)
// 创建并暴露一个路由器
export default new VueRouter({
routes:[//注:此处的方法名,记住这里是routes,不是routers,没有r,要是写成routers,控制台不会报错,就是渲染不出组件来,牢记啊!不然会让人崩溃的
{// 一级路由
name: 'guanyu', // 命名路由:简化跳转编码
path:'/about',
component: About,//注:此处容易跟着代码提示一不小心写成components,要注意,控制台报错TypeError: Cannot read property '$createElement' of undefined
}, // 一组路由KV对
{// 一级路由
path: '/Home',
component: Home,
// 子组件二级路由数组:一级路由下二级路由
children: [ // 子路由:可能N多个,所以使用数组
path: 'news', // 子路由:默认有‘/',遍历children时自动增加’/'
component: News,
path: 'message',
component: Message,
children:[//三级路由:message子组件的子组件Detail
name:'xiangqing', // 命名路由:简化跳转编码
// path: 'detail', // query传参
path: 'detail/:id/:title', // params传参:id/:title占位符
component: Detail,
}, // 一组路由KV对
], // 数组管理一堆route路由
2. 传递参数
message.vue中
<template>
<!--v-for 必须配 :key-->
<li v-for="m in messageList" :key="m.id">
<!--<a href="/message1">{{ m.title }}</a> -->
<!--使用router路由:to='完整路由'-->
<!--<router-link to="/home/message/detail">{{ m.title }}</router-link> -->
<!--query传参:to='完整路由?id=666&title=你好啊!'-->
<!--<router-link to="/home/message/detail?id=666&title=你好啊!">{{ m.title }}</router-link> -->
<!--跳转路由并携带query参数,to的字符串写法,
带有js参数${m.id}的:解析需要绑定:to="``",注意:``-->
<!--<router-link :to="`/home/message/detail?id=${m.id}&title=${m.title}`">{{ m.title }}</router-link> -->
<!-- 跳转路由并携带params参数,to的字符串写法 -->
<!--<router-link :to="`/home/message/detail/${m.id}/${m.title}`">{{ m.title }}</router-link> -->
<!-- 跳转路由并携带query参数或者params参数,to的对象写法(推荐) -->
<router-link :to="{
// 编写2个属性
// path: '/home/message/detail', // 路由路径
name: 'xiangqing', // 命名路由index.ts中:简化跳转编码效果同上
// query: { // query对象传参
params: { // params对象传参,注:必须使用name:命名路由不能使用path:
id: m.id,
title: m.title,
{{ m.title }}
</router-link>
<router-view></router-view>
</div>
</template>
<script>
export default {
name: 'Message',
data(){
return{
messageList:[
// 可以使用axios从服务器获取数据
{id:'001',title:'消息001'},
{id:'002',title:'消息002'},
{id:'003',title:'消息003'},
</script>
<style scoped>
</style>
> 特别注意:路由携带params参数时,若使用to的对象写法,则不能使用path配置项,必须使用name配置!
3. 接收参数:
<!--<li>消息编号:{{ $route.query.id }}</li>-->
<!--params方式-->
<li>消息编号:{{ $route.params.id }}</li>
<!--<li>消息标题:{{ $route.query.title }}</li>-->
<li>消息标题:{{ $route.params.title }}</li>
</template>
<script>
export default {
name: 'Detail',
mounted(){
// console.log(this.$route) // 获取当前路由信息
</script>
<style scoped>
</style>
7.路由的props配置
作用:让路由组件更方便的收到参数
index.vue中
path: 'message',// 子路由:可能N多个,所以使用数组
component: Message,
children:[//三级路由:message子组件的子组件Detail
name:'xiangqing', // 命名路由:简化跳转编码
// path: 'detail', // query传参
path: 'detail/:id/:title', // params传参:id/:title占位符
component: Detail,
//props的第一种写法,值为对象,对象中的所有key-value都会以props的形式传给当前Detail组件
// props: {a:1,b:'hello'}, //数据写死,不常用
//props的第二种写法,值为布尔值,若布尔值为真,就会把该路由组件收到的所有params参数,以props的形式传给Detail组件。
// props:true, //但是不接收query传参
//props的第三种写法: 值为函数
props($route){
return{id:$route.params.id,title:$route.params.title} //靠返回值,注意传参是query还是params,Message.vue属性中
Message.vue中
Detail.vue中
8.```<router-link>```的replace属性
1. 作用:控制路由跳转时操作浏览器历史记录的模式
2. 浏览器的历史记录有两种写入方式:分别为```push```和```replace```,```push```是追加历史记录,```replace```是替换当前记录。路由跳转时候默认为```push```
<!--v-for 必须配 :key-->
<li v-for="m in messageList" :key="m.id">
<!--<a href="/message1">{{ m.title }}</a> -->
<!--使用router路由:to='完整路由'-->
<!--<router-link to="/home/message/detail">{{ m.title }}</router-link> -->
<!--query传参:to='完整路由?id=666&title=你好啊!'-->
<!--<router-link to="/home/message/detail?id=666&title=你好啊!">{{ m.title }}</router-link> -->
<!--跳转路由并携带query参数,to的字符串写法,
带有js参数${m.id}的:解析需要绑定:to="``",注意:``-->
<!--<router-link :to="`/home/message/detail?id=${m.id}&title=${m.title}`">{{ m.title }}</router-link> -->
<!-- 跳转路由并携带params参数,to的字符串写法 -->
<!--<router-link :to="`/home/message/detail/${m.id}/${m.title}`">{{ m.title }}</router-link> -->
<!-- 跳转路由并携带query参数或者params参数,to的对象写法(推荐) -->
<router-link :to="{
// 编写2个属性
// path: '/home/message/detail', // 路由路径
name: 'xiangqing', // 命名路由index.ts中:简化跳转编码效果同上
// query: { // query对象传参,使用props第三种写法
params: { // params对象传参,注:必须使用name:命名路由不能使用path:
id: m.id,
title: m.title,
{{ m.title }}
</router-link>
<!--编程式路由导航-->
<!--pushShow(m):将v-for中的m当参数传入-->
<button @click="pushShow(m)">push查看</button>
<button @click="replaceShow(m)">replace查看</button>
<router-view></router-view>
</div>
</template>
<script>
export default {
name: 'Message',
data(){
return{
messageList:[
// 可以使用axios从服务器获取数据
{id:'
001',title:'消息001'},
{id:'002',title:'消息002'},
{id:'003',title:'消息003'},
methods:{
pushShow(m){// pushShow(m):将v-for中的m当参数传入
// console.log('输出了路由器',this.$router)
// 编程式路由导航
this.$router.push({// 将router-link to中内容写入
// 编写2个属性
// path: '/home/message/detail', // 路由路径
name: 'xiangqing', // 命名路由index.ts中:简化跳转编码效果同上
// query: { // query对象传参,使用props第三种写法
params: { // params对象传参,注:必须使用name:命名路由不能使用path:
id: m.id,
title: m.title,
replaceShow(m){
// console.log('输出了路由器',this.$router)
// 编程式路由导航
this.$router.replace({// 将router-link to中内容写入
name: 'xiangqing',
params: { // params对象传参,注:必须使用name:命名路由不能使用path:
id: m.id,
title: m.title,
</script>
<style scoped>
</style>
一般路由:Banner.vue
<template>
<div class="col-xs-offset-2 col-xs-8">
<div class="page-header">
<h2>Vue Router Demo</h2>
<button @click="back">后退</button>
<button @click="forward">前进</button>
<button @click="test">测试一下go</button>
</div>
</div>
</template>
<script>
export default {
name: 'Banner',
methods:{
back(){
this.$router.back()
forward(){
this.$router.forward()
test(){// 控制浏览器前进后退步数,3或者-3
this.$router.go(2)
// this.$router.go(-3)
</script>
<style scoped>
</style>
10.缓存路由组件
1. 作用:让不展示的News.vue路由组件保持挂载,不被销毁。数据进行缓存
2.Home.vue路由组件具体编码:注:news.vue属于Home.vue组件内容
<ul class="nav nav-tabs">
<!--<a class="list-group-item active" href="./home-news.html">News</a>-->
<!--将a标签转变成router路由标签:router-link,to='完整跳转路径‘-->
<!--注:子路由to='完整路径名’-->
<router-link class="list-group-item" active-class="active" to="/home/news">News</router-link>
<!--<a class="list-group-item " href="./home-message.html">Message</a>-->
<router-link class="list-group-item" active-class="active" to="/home/message">Message</router-link>
<!--缓存路由组件:保持活跃标签:include包含组件=‘组件名’-->
<keep-alive include="News">
<!--编写路由匹配规则:router/index.vue-->
<router-view></router-view>
</keep-alive>
</div>
</div>
</template>
<script>
export default {
name: 'Home',
/*beforeDestroy () {// 周期钩子:销毁
console.log('Home组件即将被销毁了')
mounted () {// 挂载完毕
console.log("Home挂载完毕",this)
},*/
</script>
<style scoped>
</style>
11.两个新的生命周期钩子
1. 作用:路由组件所独有的两个钩子,用于捕获路由组件的激活状态。
2. 具体名字:
1. ```activated```路由组件被激活时触发。
2. ```deactivated```路由组件失活时触发。
News.vue完整代码
<template>
<li :style="{opacity}">欢迎学习Vue</li>
<li>news001 <input type="text"></li>
<li>news002 <input type="text"></li>
<li>news003 <input type="text"></li>
</template>
<script>
export default {
name: 'News',
data(){
return {
opacity: 1
/*beforeDestroy () {// 生命钩子周期:配合组件缓存
console.log('News组件即将被销毁了!')
clearInterval(this.timer) // 销毁定时器
mounted () { // 定时器
this.timer =setInterval(()=>{
console.log('@')
this.opacity -= 0.01
if(this.opacity <= 0) this.opacity =1
},16)
} // 与beforDestroy钩子成对出现*/
activated () { //生命钩子 激活:组件中使用更灵活
this.timer =setInterval(()=>{
console.log('@')
this.opacity -= 0.01
if(this.opacity <= 0) this.opacity =1
},16)
deactivated () { //生命钩子 失活 与activated成对出现
clearInterval(this.timer) // 销毁定时器
</script>
<style scoped>
</style>
3、回顾还有个生命周期钩子:nextTick
// nextTick:下一次 DOM 更新结束后执行
// 当改变数据后,要基于更新后的新DOM进行某些操作时,要在nextTick所指定的回调函数中执行
this.$nextTick(function (){
this.$refs.inputTitle.focus()
12.路由守卫(重点):开发常用
1. 作用:对路由进行权限控制
2. 分类:全局守卫、独享守卫、组件内守卫
3. 全局守卫:
router/index.ts完整代码
import Vue from 'vue' //注:这句必须要有,虽然在main.js里面已经引入过Vue,但是这里不要这句的话,就直接报错了Vue is not defined
// 该文件专门用于创建整个应用的路由器
import VueRouter, { RouteConfig } from 'vue-router'
// 引入组件
import About from '@/pages/About.vue'
import Home from '@/pages/Home.vue'
import News from '@/pages/News.vue'
import Message from '../pages/Message.vue'
import Detail from '@/pages/Detail.vue'
Vue.use(VueRouter)
// 创建并暴露一个路由器
// export default new VueRouter({
// 使用路由守卫时:先创建
const router = new VueRouter({
routes:[//注:此处的方法名,记住这里是routes,不是routers,没有r,要是写成routers,控制台不会报错,就是渲染不出组件来,牢记啊!不然会让人崩溃的
{// 一级路由
name: 'guanyu', // 命名路由:简化跳转编码
path:'/about',
component: About,//注:此处容易跟着代码提示一不小心写成components,要注意,控制台报错TypeError: Cannot read property '$createElement' of undefined
meta:{// 路由元信息:程序员自定义
// isAuth:true, // 是否条件访问授权:是
title:'关于',
}, // 一组路由KV对
{// 一级路由
name: 'zhuye',
path: '/Home',
component: Home,
meta:{// 路由元信息:程序员自定义
// isAuth:true, // 是否条件访问授权:是
title:'主页',
// 子组件二级路由数组:一级路由下二级路由
children: [ // 子路由:可能N多个,所以使用数组
name:'xinwen',
path: 'news', // 子路由:默认有‘/',遍历children时自动增加’/'
component: News,
meta:{// 路由元信息:程序员自定义
isAuth:true, // 是否授权:是
title:'新闻',
name:'xiaoxi',
path: 'message',
component: Message,
meta:{// 路由元信息:程序员自定义
isAuth:false, // 是否授权:是
title: '消息',
// 可能N多个,所以使用数组
children:[//三级路由:message子组件的子组件Detail
name:'xiangqing', // 命名路由:简化跳转编码
// path: 'detail', // query传参
path: 'detail/:id/:title', // params传参:id/:title占位符
component: Detail,
meta: {
title: '详情',
//props的第一种写法,值为对象,对象中的所有key-value都会以props的形式传给当前Detail组件
// props: {a:1,b:'hello'}, //数据写死,不常用
//props的第二种写法,值为布尔值,若布尔值为真,就会把该路由组件收到的所有params参数,以props的形式传给Detail组件。
// props:true, //但是不接收query传参
//props的第三种写法: 值为函数
props($route){
return{id:$route.params.id,title:$route.params.title} //靠返回值,注意传参是query还是params
}, // 一组路由KV对
], // 数组管理一堆route路由
// 使用路由器守卫时;beforeEach,before什么之前Each每一次(个)路由之前
// 全局 前置 路由守卫————初始化的时候被调用、每次路由切换之前被调用
// 参数:to去哪,form来自,next放行
router.beforeEach((to, from, next) =>{
console.log('前置路由守卫',to,from)
// document.title = to.meta.title || '通达系统' //此处有bug,受限访问的title不受限,解决放在放行之前
// if (to.path === '/home/news' || to.path === '/home/message'){
// if (to.name === 'xinwen' || to.name === 'atguigu'){
// 路由元信息:鉴权
if (to.meta.isAuth){// 判断是否需要鉴权
表单提交验证
localStorage.getItem(key):获取指定key本地存储的值; // 获取指定key 本地存储数据的值。
localStorage.setItem(key,value):将value存储到key字段; // 获取指定value 存储到key 字段
localStorage.removeItem(key):删除指定key本地存储的值; // 删除指定key 本地存储的值*/
if (localStorage.getItem('school')==='atguigu'){
/*//此处有bug,受限访问的title不受限,解决放在放行之前,但是写2遍繁琐,直接后置路由守卫优化
document.title = to.meta.title || '通达系统' */
next()
}else {
alert('学校名不对,无权查看!')
}else {
/*//此处有bug,受限访问的title不受限,解决放在放行之前,但是写2遍繁琐,直接后置路由守卫优化
document.title = to.meta.title || '通达系统' */
next()
// 全局 后置 路由守卫————初始化的时候被调用、每次路由切换之后被调用执行
router.afterEach((to, from)=>{
console.log('后置路由守卫',to,from)
document.title = to.meta.title || '通达系统' // 优化
// 暴露路由器
export default router
4. 独享守卫:
{// 一级路由
name: 'zhuye',
path: '/Home',
component: Home,
meta:{// 路由元信息:程序员自定义
// isAuth:true, // 是否条件访问授权:是
title:'主页',
// 子组件二级路由数组:一级路由下二级路由
children: [ // 子路由:可能N多个,所以使用数组
name:'xinwen',
path: 'news', // 子路由:默认有‘/',遍历children时自动增加’/'
component: News,
meta:{// 路由元信息:程序员自定义
isAuth:true, // 是否授权:是
title:'新闻',
// 独享(局部)路由守卫:进入之前enter
// 重点:独享(局部)路由守卫没有后置路由守卫!但是可以匹配全局后置路由守卫
beforeEnter:(to, from, next)=>{
// 逻辑与全局 前置 路由守卫一致
console.log('独享局部路由守卫',to,from)
// document.title = to.meta.title || '通达系统' //此处有bug,受限访问的title不受限,解决放在放行之前
// if (to.path === '/home/news' || to.path === '/home/message'){
// if (to.name === 'xinwen' || to.name === 'atguigu'){
// 路由元信息:鉴权
if (to.meta.isAuth){// 判断是否需要鉴权
表单提交验证
localStorage.getItem(key):获取指定key本地存储的值; // 获取指定key 本地存储数据的值。
localStorage.setItem(key,value):将value存储到key字段; // 获取指定value 存储到key 字段
localStorage.removeItem(key):删除指定key本地存储的值; // 删除指定key 本地存储的值*/
if (localStorage.getItem('school')==='atguigu'){
//此处有bug,受限访问的title不受限,解决放在放行之前,但是写2遍繁琐,直接后置路由守卫优化
document.title = to.meta.title || '通达系统'
next()
}else {
alert('学校名不对,无权查看!')
}else {
//此处有bug,受限访问的title不受限,解决放在放行之前,但是写2遍繁琐,直接后置路由守卫优化
document.title = to.meta.title || '通达系统'
next()
5. 组件内守卫:
13.路由器的两种工作模式
1. 对于一个url来说,什么是hash值?—— #及其后面的内容就是hash值。
2. hash值不会包含在 HTTP 请求中,即:hash值不会带给服务器。
3. hash模式:router/index.ts
mode: 'hash',
// 引入express
const express = require('express')
const history = require('connect-history-api-fallback');
const app = express()
app.use(history())
app.use(express.static(__dirname+'/static'))
// 路由
app.get('/person',(req,res)=>{
res.send({ // 给客户端返回对象:名字和年龄
name:'tom',
age:18
// 创建app实例对象
// listen端口监听(端口号,(错误)进行回调)=》{//函数体}
app.listen(5005,(err)=>{
if(!err) console.log('服务器启动成功了!')
服务器运行命令: node server
2、创建Static静态资源文件夹: 将dist打包内的文件放入
刷新报错:
解决方法: 可将前端mode:'history'变更‘hash’从新打包发布
npm run build
解决mode:'history'打包后出现404问题
1、后端工程师:进行路由匹配,方式一
2、方式二:安装服务器中间件connect-history-api-fallback
引入中间件:
3、方式三:使用nginx
第 7 章:Vue UI 组件库
7.1 移动端常用 UI 组件库
1. Vant https://youzan.github.io/vant
2. Cube UI https://didi.github.io/cube-ui
3. Mint UI http://mint-ui.github.io
4. Nutui https://nutui.jd.com/ 京东
7.2 PC 端常用 UI 组件库
1. Element UI https://element.eleme.cn 饿了么UI组件库
2. IView UI https://www.iviewui.com
7.3 安装element-ui
注意:安装时 -S :为生产依赖,-D:为开发依赖
1、安装 https://element.eleme.cn
npm i element-ui -S
2、快速上手
在 main.js 中写入以下内容:
//引入Vue
import Vue from 'vue'
//引入App
import App from './App.vue'
//引入插件
// import vueResource from 'vue-resource'
//引入store模块化Vuex
// import store from './store'
// 引入路由
import VueRouter from 'vue-router'
// 引入路由器
import router from './router' //import后面的router只能写成router,且首字母大写都不行,不然在下面new Vue里面注入的时候控制台会报错Cannot read property 'matched'
//完整引入
//引入ElementUI插件组件库
import ElementUI from 'element-ui';
//引入ElementUI全部样式
import 'element-ui/lib/theme-chalk/index.css';
//按需引入
// import { Button,Row,DatePicker } from 'element-ui';
//关闭Vue的生产提示
Vue.config.productionTip = false
// 注册使用插件
// Vue.use(vueResource)
// 注册应用路由
Vue.use(VueRouter)
// 注册应用插件组件库
Vue.use(ElementUI)
//创建vm
new Vue({
// store, // 模块化配置项:同名触发简写store:store,
render: h => h(App),
router:router, // 固定写法:@/router,记得在这里注入引入的router
/*beforeCreate() {
Vue.prototype.$bus = this // 开启全局总线
}).$mount("#app")
复制代码,
使用type、plain、round和circle属性来定义 Button 的样式。
<el-row><el-button>默认按钮</el-button><el-button type="primary">主要按钮</el-button><el-button type="success">成功按钮</el-button><el-button type="info">信息按钮</el-button><el-button type="warning">警告按钮</el-button><el-button type="danger">危险按钮</el-button>
<el-button type="primary" icon="el-icon-edit"></el-button> //type、icon查询相关Attrbutes属性表</el-row>
查询样式最后面的Attributes属性表
参数说明类型可选值默认值
string
medium / small / mini
string
primary / success / warning / danger / info / text
plain
是否朴素按钮
boolean
false
round
是否圆角按钮
boolean
false
circle
是否圆形按钮
boolean
false
loading
是否加载中状态
boolean
false
disabled
是否禁用状态
boolean
false
string
autofocus
是否默认聚焦
boolean
false
native-type
原生 type 属性
string
button / submit / reset
button
4、快速上手中:按需引入
借助 babel-plugin-component,我们可以只引入需要的组件,以达到减小项目体积的目的。
首先,安装 babel-plugin-component: -D:为开发依赖
npm install babel-plugin-component -D
然后,将 .babelrc 修改为:注意Vue-cli脚手架项目文件为babel.config.js
"presets": [["es2015", { "modules": false }]],
"plugins": [
"component",
"libraryName": "element-ui",
"styleLibraryName": "theme-chalk"
避免破坏源文件,只修改添加
module.exports = {
presets: [
'@vue/cli-plugin-babel/preset',
["es2015", { "modules": false }]
"plugins": [
"component",
"libraryName": "element-ui",
"styleLibraryName": "theme-chalk"
接下来,如果你只希望引入部分组件,比如 Button 和 Select,那么需要在 main.js 中写入以下内容:注意 不用加el- 组件名第一字母大写,
//引入Vue
import Vue from 'vue'
//引入App
import App from './App.vue'
//引入插件
// import vueResource from 'vue-resource'
//引入store模块化Vuex
// import store from './store'
// 引入路由
import VueRouter from 'vue-router'
// 引入路由器
import router from './router' //import后面的router只能写成router,且首字母大写都不行,不然在下面new Vue里面注入的时候控制台会报错Cannot read property 'matched'
//完整引入
//引入ElementUI组件库
// import ElementUI from 'element-ui';
//引入ElementUI全部样式
// import 'element-ui/lib/theme-chalk/index.css';
//按需引入
// import { Button,Row,DatePicker } from 'element-ui';
import {Button,Row,DatePicker} from 'element-ui'
//关闭Vue的生产提示
Vue.config.productionTip = false
// 注册使用插件
// Vue.use(vueResource)
// 注册应用路由
Vue.use(VueRouter)
// 注册完整应用组件库
// Vue.use(ElementUI)
// 组件id自定义
Vue.component('Button-name',Button);
Vue.component('julan-row',Row);
Vue.component('tongda-datepicker',DatePicker);
/* 或写为
* Vue.use(Button)
* Vue.use(Row)
* Vue.use(Button,Row,DatePicker)
//创建vm
new Vue({
// store, // 模块化配置项:同名触发简写store:store,
render: h => h(App),
router:router, // 固定写法:@/router,记得在这里注入引入的router
/*beforeCreate() {
Vue.prototype.$bus = this // 开启全局总线
}).$mount("#app")
按需引入的问题
not found XXX: 解决 npm i XXX
解决:修改babel.config.js
module.exports = {
presets: [
'@vue/cli-plugin-babel/preset',
["@babel/preset-env", { "modules": false }]
"plugins": [
"component",
"libraryName": "element-ui",
"styleLibraryName": "theme-chalk"
- 2020年9月18日,Vue.js发布3.0版本,代号:One Piece(海贼王)
- 耗时2年多、[2600+次提交](https://github.com/vuejs/vue-next/graphs/commit-activity)、[30+个RFC](https://github.com/vuejs/rfcs/tree/master/active-rfcs)、
[600+次PR](https://github.com/vuejs/vue-next/pulls?q=is%3Apr+is%3Amerged+-author%3Aapp%2Fdependabot-preview+)、[99位贡献者](https://github.com/vuejs/vue-next/graphs/contributors)
- github上的tags地址:https://github.com/vuejs/vue-next/releases/tag/v3.0.0
## 2.Vue3带来了什么
### 1.性能的提升
- 打包大小减少41%
- 初次渲染快55%, 更新渲染快133%
- 内存减少54%
......
### 2.源码的升级
- 使用Proxy代替defineProperty实现响应式
- 重写虚拟DOM的实现和Tree-Shaking
......
### 3.拥抱TypeScript
- Vue3可以更好的支持TypeScript
### 4.新的特性
1. Composition API(组合API)
- setup配置
- ref与reactive
- watch与watchEffect
- provide与inject
- ......
2. 新的内置组件
- Fragment
- Teleport
- Suspense
3. 其他改变
- 新的生命周期钩子
- data 选项应始终被声明为一个函数
- 移除keyCode支持作为 v-on 的修饰符
- ......
# 一、创建Vue3.0工程
## 1.使用 vue-cli 创建
官方文档:https://cli.vuejs.org/zh/guide/creating-a-project.html#vue-create
```bash
## 查看@vue/cli版本,确保@vue/cli版本在4.5.0以上
vue --version
## 安装或者升级你的@vue/cli
npm install -g @vue/cli
## 创建
vue create vue_test
## 启动
cd vue_test
npm run serve
## 2.使用 vite 创建
官方文档:https://v3.cn.vuejs.org/guide/installation.html#vite
vite官网:https://vitejs.cn
- 什么是vite?—— 新一代前端构建工具。
- 优势如下:
- 开发环境中,无需打包操作,可快速的冷启动。
- 轻量快速的热重载(HMR)。
- 真正的按需编译,不再等待整个应用编译完成。
- 传统构建 与 vite构建对比图
<img src="https://cn.vitejs.dev/assets/bundler.37740380.png" style="width:500px;height:280px;float:left" />
<img src="https://cn.vitejs.dev/assets/esm.3070012d.png" style="width:480px;height:280px" />
```bash
## 创建工程
npm init vite-app <project-name>
## 进入工程目录
cd <project-name>
## 安装依赖
npm install
## 运行
npm run dev
# 二、常用 Composition API
官方文档: https://v3.cn.vuejs.org/guide/composition-api-introduction.html
## 1.拉开序幕的setup
1. 理解:Vue3.0中一个新的配置项,值为一个函数。
2. setup是所有<strong style="color:#DD5145">Composition API(组合API)</strong><i style="color:gray;font-weight:bold">“ 表演的舞台 ”</i>。
4. 组件中所用到的:数据、方法等等,均要配置在setup中。
5. setup函数的两种返回值:
1. 若返回一个对象,则对象中的属性、方法, 在模板中均可以直接使用。(重点关注!)
2. <span style="color:#aad">若返回一个渲染函数:则可以自定义渲染内容。(了解)</span>
6. 注意点:
1. 尽量不要与Vue2.x配置混用
- Vue2.x配置(data、methos、computed...)中<strong style="color:#DD5145">可以访问到</strong>setup中的属性、方法。
- 但在setup中<strong style="color:#DD5145">不能访问到</strong>Vue2.x配置(data、methos、computed...)。
- 如果有重名, setup优先。
2. setup不能是一个async函数,因为返回值不再是return的对象, 而是promise, 模板看不到return对象中的属性。(后期也可以返回一个Promise实例,但需要Suspense和异步组件的配合)
## 2.ref函数
- 作用: 定义一个响应式的数据
- 语法: ```const xxx = ref(initValue)```
- 创建一个包含响应式数据的<strong style="color:#DD5145">引用对象(reference对象,简称ref对象)</strong>。
- JS中操作数据: ```xxx.value```
- 模板中读取数据: 不需要.value,直接:```<div>{{xxx}}</div>```
- 备注:
- 接收的数据可以是:基本类型、也可以是对象类型。
- 基本类型的数据:响应式依然是靠``Object.defineProperty()``的```get```与```set```完成的。
- 对象类型的数据:内部 <i style="color:gray;font-weight:bold">“ 求助 ”</i> 了Vue3.0中的一个新函数—— ```reactive```函数。
## 3.reactive函数
- 作用: 定义一个<strong style="color:#DD5145">对象类型</strong>的响应式数据(基本类型不要用它,要用```ref```函数)
- 语法:```const 代理对象= reactive(源对象)```接收一个对象(或数组),返回一个<strong style="color:#DD5145">代理对象(Proxy的实例对象,简称proxy对象)</strong>
- reactive定义的响应式数据是“深层次的”。
- 内部基于 ES6 的 Proxy 实现,通过代理对象操作源对象内部数据进行操作。
## 4.Vue3.0中的响应式原理
### vue2.x的响应式
- 实现原理:
- 对象类型:通过```Object.defineProperty()```对属性的读取、修改进行拦截(数据劫持)。
- 数组类型:通过重写更新数组的一系列方法来实现拦截。(对数组的变更方法进行了包裹)。
```js
Object.defineProperty(data, 'count', {
get () {},
set () {}
- 存在问题:
- 新增属性、删除属性, 界面不会更新。
- 直接通过下标修改数组, 界面不会自动更新。
### Vue3.0的响应式
- 实现原理:
-
通过Proxy(代理): 拦截对象中任意属性的变化, 包括:属性值的读写、属性的添加、属性的删除等。
- 通过Reflect(反射): 对源对象的属性进行操作。
- MDN文档中描述的Proxy与Reflect:
- Proxy:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Proxy
- Reflect:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Reflect
```js
new Proxy(data, {
// 拦截读取属性值
get (target, prop) {
return Reflect.get(target, prop)
// 拦截设置属性值或添加新属性
set (target, prop, value) {
return Reflect.set(target, prop, value)
// 拦截删除属性
deleteProperty (target, prop) {
return Reflect.deleteProperty(target, prop)
proxy.name = 'tom'
## 5.reactive对比ref
- 从定义数据角度对比:
- ref用来定义:<strong style="color:#DD5145">基本类型数据</strong>。
- reactive用来定义:<strong style="color:#DD5145">对象(或数组)类型数据</strong>。
- 备注:ref也可以用来定义<strong style="color:#DD5145">对象(或数组)类型数据</strong>, 它内部会自动通过```reactive```转为<strong style="color:#DD5145">代理对象</strong>。
- 从原理角度对比:
- ref通过``Object.defineProperty()``的```get```与```set```来实现响应式(数据劫持)。
- reactive通过使用<strong style="color:#DD5145">Proxy</strong>来实现响应式(数据劫持),
并通过<strong style="color:#DD5145">Reflect</strong>操作<strong style="color:orange">源对象</strong>内部的数据。
- 从使用角度对比:
- ref定义的数据:操作数据<strong style="color:#DD5145">需要</strong>```.value```,读取数据时模板中直接读取<strong style="color:#DD5145">不需要</strong>```.value```。
- reactive定义的数据:操作数据与读取数据:<strong style="color:#DD5145">均不需要</strong>```.value```。
## 6.setup的两个注意点
- setup执行的时机
- 在beforeCreate之前执行一次,this是undefined。
- setup的参数
- props:值为对象,包含:组件外部传递过来,且组件内部声明接收了的属性。
- context:上下文对象
- attrs: 值为对象,包含:组件外部传递过来,但没有在props配置中声明的属性, 相当于 ```this.$attrs```。
- slots: 收到的插槽内容, 相当于 ```this.$slots```。
- emit: 分发自定义事件的函数, 相当于 ```this.$emit```。
## 7.计算属性与监视
### 1.computed函数
- 与Vue2.x中computed配置功能一致
```js
import {computed} from 'vue'
setup(){
//计算属性——简写
let fullName = computed(()=>{
return person.firstName + '-' + person.lastName
//计算属性——完整
let fullName = computed({
get(){
return person.firstName + '-' + person.lastName
set(value){
const nameArr = value.split('-')
person.firstName = nameArr[0]
person.lastName = nameArr[1]
### 2.watch函数
- 与Vue2.x中watch配置功能一致
- 两个小“坑”:
- 监视reactive定义的响应式数据时:oldValue无法正确获取、强制开启了深度监视(deep配置失效)。
- 监视reactive定义的响应式数据中某个属性时:deep配置有效。
```js
//情况一:监视ref定义的响应式数据
watch(sum,(newValue,oldValue)=>{
console.log('sum变化了',newValue,oldValue)
},{immediate:true})
//情况二:监视多个ref定义的响应式数据
watch([sum,msg],(newValue,oldValue)=>{
console.log('sum或msg变化了',newValue,oldValue)
/* 情况三:监视reactive定义的响应式数据
若watch监视的是reactive定义的响应式数据,则无法正确获得oldValue!!
若watch监视的是reactive定义的响应式数据,则强制开启了深度监视
watch(person,(newValue,oldValue)=>{
console.log('person变化了',newValue,oldValue)
},{immediate:true,deep:false}) //此处的deep配置不再奏效
//情况四:监视reactive定义的响应式数据中的某个属性
watch(()=>person.job,(newValue,oldValue)=>{
console.log('person的job变化了',newValue,oldValue)
},{immediate:true,deep:true})
//情况五:监视reactive定义的响应式数据中的某些属性
watch([()=>person.job,()=>person.name],(newValue,oldValue)=>{
console.log('person的job变化了',newValue,oldValue
)
},{immediate:true,deep:true})
//特殊情况
watch(()=>person.job,(newValue,oldValue)=>{
console.log('person的job变化了',newValue,oldValue)
},{deep:true}) //此处由于监视的是reactive素定义的对象中的某个属性,所以deep配置有效
### 3.watchEffect函数
- watch的套路是:既要指明监视的属性,也要指明监视的回调。
- watchEffect的套路是:不用指明监视哪个属性,监视的回调中用到哪个属性,那就监视哪个属性。
- watchEffect有点像computed:
- 但computed注重的计算出来的值(回调函数的返回值),所以必须要写返回值。
- 而watchEffect更注重的是过程(回调函数的函数体),所以不用写返回值。
```js
//watchEffect所指定的回调中用到的数据只要发生变化,则直接重新执行回调。
watchEffect(()=>{
const x1 = sum.value
const x2 = person.age
console.log('watchEffect配置的回调执行了')
## 8.生命周期
<div style="border:1px solid black;width:380px;float:left;margin-right:20px;"><strong>vue2.x的生命周期</strong>
<img src="https://cn.vuejs.org/images/lifecycle.png" alt="lifecycle_2" style="zoom:33%;width:1200px" />
</div><div style="border:1px solid black;width:510px;height:985px;float:left"><strong>vue3.0的生命周期</strong>
<img src="https://v3.cn.vuejs.org/images/lifecycle.svg" alt="lifecycle_2" style="zoom:33%;width:2500px" /></div>
- Vue3.0中可以继续使用Vue2.x中的生命周期钩子,但有有两个被更名:
- ```beforeDestroy```改名为 ```beforeUnmount```
- ```destroyed```改名为 ```unmounted```
- Vue3.0也提供了 Composition API 形式的生命周期钩子,与Vue2.x中钩子对应关系如下:
- `beforeCreate`===>`setup()`
- `created`=======>`setup()`
- `beforeMount` ===>`onBeforeMount`
- `mounted`=======>`onMounted`
- `beforeUpdate`===>`onBeforeUpdate`
- `updated` =======>`onUpdated`
- `beforeUnmount` ==>`onBeforeUnmount`
- `unmounted` =====>`onUnmounted`
## 9.自定义hook函数
- 什么是hook?—— 本质是一个函数,把setup函数中使用的Composition API进行了封装。
- 类似于vue2.x中的mixin。
- 自定义hook的优势: 复用代码, 让setup中的逻辑更清楚易懂。
## 10.toRef
- 作用:创建一个 ref 对象,其value值指向另一个对象中的某个属性。
- 语法:```const name = toRef(person,'name')```
- 应用: 要将响应式对象中的某个属性单独提供给外部使用时。
- 扩展:```toRefs``` 与```toRef```功能一致,但可以批量创建多个 ref 对象,语法:```toRefs(person)```
# 三、其它 Composition API
## 1.shallowReactive 与 shallowRef
- shallowReactive:只处理对象最外层属性的响应式(浅响应式)。
- shallowRef:只处理基本数据类型的响应式, 不进行对象的响应式处理。
- 什么时候使用?
- 如果有一个对象数据,结构比较深, 但变化时只是外层属性变化 ===> shallowReactive。
- 如果有一个对象数据,后续功能不会修改该对象中的属性,而是生新的对象来替换 ===> shallowRef。
## 2.readonly 与 shallowReadonly
- readonly: 让一个响应式数据变为只读的(深只读)。
- shallowReadonly:让一个响应式数据变为只读的(浅只读)。
- 应用场景: 不希望数据被修改时。
## 3.toRaw 与 markRaw
- toRaw:
- 作用:将一个由```reactive```生成的<strong style="color:orange">响应式对象</strong>转为<strong style="color:orange">普通对象</strong>。
- 使用场景:用于读取响应式对象对应的普通对象,对这个普通对象的所有操作,不会引起页面更新。
- markRaw:
- 作用:标记一个对象,使其永远不会再成为响应式对象。
- 应用场景:
1. 有些值不应被设置为响应式的,例如复杂的第三方类库等。
2. 当渲染具有不可变数据源的大列表时,跳过响应式转换可以提高性能。
## 4.customRef
- 作用:创建一个自定义的 ref,并对其依赖项跟踪和更新触发进行显式控制。
- 实现防抖效果:
```vue
<template>
<input type="text" v-model="keyword">
<h3>{{keyword}}</h3>
</template>
<script>
import {ref,customRef} from 'vue'
export default {
name:'Demo',
setup(){
// let keyword = ref('hello') //使用Vue准备好的内置ref
//自定义一个myRef
function myRef(value,delay){
let timer
//通过customRef去实现自定义
return customRef((track,trigger)=>{
return{
get(){
track() //告诉Vue这个value值是需要被“追踪”的
return value
set(newValue){
clearTimeout(timer)
timer = setTimeout(()=>{
value = newValue
trigger() //告诉Vue去更新界面
},delay)
let keyword = myRef('hello',500) //使用程序员自定义的ref
return {
keyword
</script>
<img src="https://v3.cn.vuejs.org/images/components_provide.png" style
="width:300px" />
- 作用:实现<strong style="color:#DD5145">祖与后代组件间</strong>通信
- 套路:父组件有一个 `provide` 选项来提供数据,后代组件有一个 `inject` 选项来开始使用这些数据
- 具体写法:
1. 祖组件中:
```js
setup(){
......
let car = reactive({name:'奔驰',price:'40万'})
provide('car',car)
......
2. 后代组件中:
```js
setup(props,context){
......
const car = inject('car')
return {car}
......
## 6.响应式数据的判断
- isRef: 检查一个值是否为一个 ref 对象
- isReactive: 检查一个对象是否是由 `reactive` 创建的响应式代理
- isReadonly: 检查一个对象是否是由 `readonly` 创建的只读代理
- isProxy: 检查一个对象是否是由 `reactive` 或者 `readonly` 方法创建的代理
# 四、Composition API 的优势
## 1.Options API 存在的问题
使用传统OptionsAPI中,新增或者修改一个需求,就需要分别在data,methods,computed里修改 。
<div style="width:600px;height:370px;overflow:hidden;float:left">
<img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/f84e4e2c02424d9a99862ade0a2e4114~tplv-k3u1fbpfcp-watermark.image" style="width:600px;float:left" />
</div>
<div style="width:300px;height:370px;overflow:hidden;float:left">
<img src="https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/e5ac7e20d1784887a826f6360768a368~tplv-k3u1fbpfcp-watermark.image" style="zoom:50%;width:560px;left" />
</div>
## 2.Composition API 的优势
我们可以更加优雅的组织我们的代码,函数。让相关功能的代码更加有序的组织在一起。
<div style="width:500px;height:340px;overflow:hidden;float:left">
<img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/bc0be8211fc54b6c941c036791ba4efe~tplv-k3u1fbpfcp-watermark.image"style="height:360px"/>
</div>
<div style="width:430px;height:340px;overflow:hidden;float:left">
<img src="https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/6cc55165c0e34069a75fe36f8712eb80~tplv-k3u1fbpfcp-watermark.image"style="height:360px"/>
</div>
# 五、新的组件
## 1.Fragment
- 在Vue2中: 组件必须有一个根标签
- 在Vue3中: 组件可以没有根标签, 内部会将多个标签包含在一个Fragment虚拟元素中
- 好处: 减少标签层级, 减小内存占用
## 2.Teleport
- 什么是Teleport?—— `Teleport` 是一种能够将我们的<strong style="color:#DD5145">组件html结构</strong>移动到指定位置的技术。
```vue
<teleport to="移动位置">
<div v-if="isShow" class="mask">
<div class="dialog">
<h3>我是一个弹窗</h3>
<button @click="isShow = false">关闭弹窗</button>
</teleport>
## 3.Suspense
- 等待异步组件时渲染一些额外内容,让应用有更好的用户体验
- 使用步骤:
- 异步引入组件
```js
import {defineAsyncComponent} from 'vue'
const Child = defineAsyncComponent(()=>import('./components/Child.vue'))
- 使用```Suspense```包裹组件,并配置好```default``` 与 ```fallback```
```vue
<template>
<div class="app">
<h3>我是App组件</h3>
<Suspense>
<template v-slot:default>
<Child/>
</template>
<template v-slot:fallback>
<h3>加载中.....</h3>
</template>
</Suspense>
</template>
# 六、其他
## 1.全局API的转移
- Vue 2.x 有许多全局 API 和配置。
- 例如:注册全局组件、注册全局指令等。
```js
//注册全局组件
Vue.component('MyButton', {
data: () => ({
count: 0
template: '<button @click="count++">Clicked {{count}} times.</button>'
//注册全局指令
Vue.directive('focus', {
inserted: el => el.focus()
- Vue3.0中对这些API做出了调整:
- 将全局的API,即:```Vue.xxx```调整到应用实例(```app```)上
| 2.x 全局 API(```Vue```) | 3.x 实例 API (`app`) |
| ------------------------- | ------------------------------------------- |
| Vue.config.xxxx | app.config.xxxx |
| Vue.config.productionTip | <strong style="color:#DD5145">移除</strong> |
| Vue.component | app.component |
| Vue.directive | app.directive |
| Vue.mixin | app.mixin |
| Vue.use | app.use |
| Vue.prototype | app.config.globalProperties |
## 2.其他改变
- data选项应始终被声明为一个函数。
- 过度类名的更改:
- Vue2.x写法
```css
.v-enter,
.v-leave-to {
opacity: 0;
.v-leave,
.v-enter-to {
opacity: 1;
- Vue3.x写法
```css
.v-enter-from,
.v-leave-to {
opacity: 0;
.v-leave-from,
.v-enter-to {
opacity: 1;
- <strong style="color:#DD5145">移除</strong>keyCode作为 v-on 的修饰符,同时也不再支持```config.keyCodes```
- <strong style="color:#DD5145">移除</strong>```v-on.native```修饰符
- 父组件中绑定事件
```vue
<my-component
v-on:close="handleComponentEvent"
v-on:click="handleNativeClickEvent"
- 子组件中声明自定义事件
```vue
<script>
export default {
emits: ['close']
</script>
- <strong style="color:#DD5145">移除</strong>过滤器(filter)
> 过滤器虽然这看起来很方便,但它需要一个自定义语法,打破大括号内表达式是 “只是 JavaScript” 的假设,这不仅有学习成本,而且有实现成本!建议用方法调用或计算属性去替换过滤器。
- ......
|
|
爱搭讪的开水瓶 · Linux 变量的使用 - 刘合栋 - 博客园 2 年前 |