vue-cli(基础篇):知识点集合

44 阅读6分钟
vuecli 的 tips
  • 1.开启终端
    ctrl+esc下面的键
    或者点击终端-新建终端
    
  • 2.关闭eslint
    vue.config.js 里配置 lintOnSave:false 重启项目
    
  • 3.解析顺序
    import 再 配置项 再 渲染
    
  • 4.npm相关
    可以查询有多少版本 npm view webpack version 
    less-loader      npm view less-loader version   (567里选建议7)
    
1.ref 获取dom元素/获取组件的实例对象vc
  • 1.写法
<!-- this.$refs.title 获取dom元素 -->
<h1 v-text="msg2" ref="title"></h1>
<!-- this.$refs.school 获取组件的实例对象 -->
<School ref="school" />

console.log(this.$refs.title); // 真实的dom元素 
console.log(this.$refs.school); // 拿到组件的实例对象 
  • 2.代码示例
/*App.vue*/
     <!-- this.$refs.title 获取dom元素 -->
    <h1 v-text="msg2" ref="title"></h1>
    <!-- this.$refs.school 获取组件的实例对象 -->
    <School ref="school" />
    <button @click="showDom">点击输出上方两个dom元素</button>
    <script>
    // 引入组件
    import School from './components/School'
    import Student from './components/Student'

    export default {
      name: 'App',
      components: {
        School,
        Student,
      },
      data() {
        return {
          msg: '拿下月薪过w的offer',
          msg2:'成功了'
        }
      },
      methods: {
        showDom() {
          console.log(this.$refs.title); // 真实的dom元素
          console.log(this.$refs.school); // 拿到组件的实例对象
          console.log(this.$refs.student); // 拿到组件的实例对象
        }
      }
    }
</script>
/*School.vue 组件*/
<template>
    <div class="demo">
        <h2>学校名称:{{ name }}</h2>
        <h2>学校地址:{{ address }}</h2>
        <button @click="showName">点我提示学校名</button>
    </div>
</template>

<script>
// 直接暴露组件的配置对象
export default {
    name: 'School',
    data() {
        return {
            name: '尚硅谷',
            address: '北京昌平'
        }
    },
    methods: {
        showName() {
            alert(this.name)
        }
    },
}
</script>

<style>
.demo {
    background-color: orange;
}
</style>
3.mixin混入 复用配置
1. 功能:可以把多个组件共用的配置提取成一个混入对象

2. 使用方式:
    第一步定义混入:
    每一个配置项都可以写进mixin.js里面 并暴露出去
    ```
    {
        data(){....},
        methods:{....}
        ....
    }
    ```
    第二步使用混入:
    全局混入 main.js里写:```Vue.mixin(xxx)``` 所有vm vc都会得到这个代码片段
    局部混入:```mixins:['xxx']	```
  • 代码示例
4.插件的使用
1.创建plugins.js文件(带方法install和参数)
    1.全局过滤器 Vue.filter('mySlice',function(val){}) 
    2.全局指令 Vue.directive('fbind',{})
    3.定义混入 Vue.mixin({})
    4.Vue原型上添加一个方法 Vue.prototype.hello = ()=>{alert('你好啊')} (vm和vc就都能用了)
  
2.引入并使用 Vue.use(plugins)
    1. import plugins from 'plugins' 引入main.js 
    2. Vue.use(plugins) 应用插件
  • 代码案例
  • main.js 入口文件引入插件并注册
import Vue from 'vue'
import App from './App.vue'
Vue.config.productionTip = false

// 引入插件
import {plugins} from './plugins'
// 应用插件
Vue.use(plugins,1,2)

new Vue({
  render: h => h(App),
}).$mount('#app')
  • plugins.js 插件文件
// 定义一个插件
export const plugins = {
    // vue的构造函数 可以写过滤器 自定义指令 mixin 
    install(Vue,a,b) {
        console.log('install',a,b, Vue)

        // 全局过滤器
        Vue.filter('mySlice', function (value) {
            return value.slice(0, 4)
        })

        //全局指令
        Vue.directive('fbind', {
            //指令与元素成功绑定时(一上来)
            bind(element, binding) {
                element.value = binding.value
            },
            //指令所在元素被插入页面时
            inserted(element, binding) {
                element.focus()
            },
            //指令所在的模板被重新解析时
            update(element, binding) {
                element.value = binding.value
            }
        })

        // 原型上添加一个方法
        Vue.prototype.hello = () => {
            alert('hello')
        }

        // 混入
        Vue.mixin({
           data(){
            return {
                plugins_msg:'plugins里面的mixin'
            }
           }
        })
    }
}
  • App.vue 使用
<template>
  <div>
    <!-- 使用插件plugins -->
    <div>
      全局混入 plugins_msg 以及 全局过滤器 显示[0,4)<br>
      {{ plugins_msg | mySlice}} <br>
      全局指令 默认获取焦点 <br>
      <input type="text" v-fbind:value="plugins_msg"><br>
      <button @click="hello">点我触发hello全局方法</button>
    </div>
  </div>
</template>
5.scoped样式 防止样式冲突
<style scoped> scoped 表示是局部样式 防止冲突 </style>
App.vue里无效 所有的组件都汇总到App里头
<style lang="css">less 或者 sass 或者 css</style>
比如用less 那么会会提示你安装less -loader 那就 npm i less-loader
6.组件-自定义事件(组件间通信 2种绑定写法)
  • 使用场景
1.A是父组件 B是子组件 BA传数据 那么就要在A中给B绑定自定义事件(事件的回调在A中)
2.通过this.$ref.xxx.$on('atguigu',回调)绑定自定义事件时,回调要么配置在methods,要么用箭头函数,
否则this指向会出问题(function的会指向就是vc了就不对了,因为事件回调应该在A中)同下ref的注意事项
  • 绑定1:@atguigu="getValue" <=> this.$emit('atguigu',this.name);
1.父组件给调用的子组件绑定自定义事件@xxx="getValue" 
2.getValue(value){console.log(value)}; // 收到子组件传递过来的value
3.子组件定义一个方法,按钮点击触发this.@emit('自定义事件名xxx',value)传递value
  • 代码示例
/* App.vue */

/* 渲染了马上atguigu自定义事件就会挂在Student身上 */

<Student v-on:atguigu="getStudentName"/>
<Student @atguigu="getStudentName"/>

/* 点击Student组件的click的时候触发atguigu就触发getStudentName */
getStudentName(name,...params){
    // 第一个参数,剩下的全都是params数组里 
    console.log(name)
}
/* Student.vue 组件 */

// vc触发Student身上自定义的atguigu事件就能调用App.vue身上的getStudentName函数
<button @click="sendStudentName">把学生名字给App</button>

sendStudentName(){
    this.$emit('atguigu',this.name,a,b,c); // 除了this.name 其它参数传过去数组接收
}
  • 绑定2:$ref
/* 需求:可以等几秒再绑定自定义事件 写在mounted钩子里 */
this.$ref.student.$on('atguigu',this.getStudentName); // 绑定自定义事件atguigu 触发getStudentName
this.$ref.student.$once('atguigu',this.getStudentName); // 只能触发一次
  • $ref注意事项
不能把父组件调用的事件写在回调里面 除非写成箭头函数 会往上层找this 就是mounted的this那就可以调用了
  • 解绑:$off 三种写法
/* Student.vue */
this.$off('atguigu'); // 解绑atguigu
this.$off(['atguigu','atguigu2','atguigu3']); // 解绑atguigu 以及atguigu2 atguigu3
this.$off(); // 全解绑
7.组件-注释事项
  • 绑定原生事件 .native 否则会当作自定义事件
<Student @click.native="show"/>
8.全局事件总线(任意组件间通信方法1 $sub) - 文件:vue_test3_custom_sub_pubsub
1. 一种组件间通信的方式,适用于<span style="color:red">任意组件间通信</span>。

2. 安装全局事件总线:

   js
   new Vue({
   	......
   	beforeCreate() {
   		Vue.prototype.$bus = this //安装全局事件总线,$bus就是当前应用的vm
   	},
       ......
   }) 
   
3. 使用事件总线:
   1. 接收数据:A组件想接收数据,则在A组件中给$bus绑定自定义事件,事件的
       <span style="color:red">回调留在A组件自身。</span>

      ```js
      methods(){
        demo(data){......}
      }
      ......
      mounted() {
        this.$bus.$on('xxxx',this.demo)
      }
      ```

   2. 提供数据:```this.$bus.$emit('xxxx',数据)```

4. 最好在beforeDestroy钩子中,用$off去解绑<span style="color:red">当前组件所用到的</span>事件。
9.消息发布与订阅(任意组件间通信方法2 pubsub) - 文件:vue_test3_custom_sub_pubsub

pubsub.js 任何框架里消息订阅发布 npm i pubsub.js

  • School.vue 接收数据
/* School.vue */
import pubsub from 'pubsub-js' // 订阅的地方要引入 
mounted(){
    this.pubId =  pubsub.subscribe('hello',(msgName,data) => {
        console.log('有人发布了hello消息,hello消息的回调执行了',msgName,data);
        console.log(this); // vc
    })
},
beforeDestroy(){
    pubsub.unsubscribe(this.pubId); // 取消订阅
}
  • Student.vue 发布数据
/* Student.vue */
import pubsub from 'pubsub-js' // 发布的地方也要引入
methods:{
    sendStudentName(){
        pubsub.publish('helllo',666); // 提供数据
    }
}
10.todoList 案例1-功能+存储 - 文件:vue_test2_todolist (props传值传方法)
  • 组件:实现界面局部功能代码和资源的集合
  • 文件:vue_test2_todolist
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不会报错,但不推荐这样做。
11.todolist 案例2-功能完整版 5种通信方式(重要且完整)
  • 文件:vue_test4_todolist_plus_edit 代码看readme.md

image.png

  • 组件通信的5种方法
1. 父子组件通信 props 接收参数和方法
2. 父子组件通信 绑定自定义事件 $ref $on 的方法
3. 父子组件通信 绑定自定义事件 @xxx this.$emit
4. 任意组件通信 全局事件总线 $sub
5. 任意组件通信 消息订阅与发布 npm i pubsub-js 插件$pubsub
  • 注意事项
props是不能修改的 
let obj = {a:1,b:2} 
obj.a = 666; // 改某个属性值 不能监测到 所以如果只是改值了 发现不了不会报错
obj = {x:100,y:200}; // 整个对象都改了 能检测到
  • 思路分析
  • header 组件
    App 组件 (对todoList增删改查操作数据 watch监听 localStorage存储)
    ------------------------------------------------------------------        
  【子传父方法1:props】(input框 取值 回车 触发事件传值给App unshift)
        input框输入:
        逻辑:获取到input的value传给App新增进todoList数组并存储
            1.获取input里的值 e.target.value 或者 v-model="title"绑定一个data
            2.构造一个新对象传给todoList 调用props过来的App.vue里的方法
            3.id用 nsnoid 精简版uuid    npm i nanoid  import {nanoid} from 'nanoid'
            4.清空input的值 this.title=''
            5.一进页面focus input 
            6.校验input的数据格式 if(!this.title.trim()) return alert('输入不能为空')
            
        item:
            1.<input type="text"  v-model="title" @keyup.enter="sendInputValue" />
            2.sendInputValue(e) {
              if(!this.title.trim()) return alert('输入不能为空')
              const todoObj = { id: nanoid(), title: this.title, done: false, edit: false };
              this.addTodoItem(todoObj)
              this.title = ''
            }
            
        App:
        新增:props父接收子(子传父方法1)
        逻辑:获取header传来的值 新增todoList 并本地存储
            1. addTodoItem(todo) 父组件定义一个方法传给子组件header接收todo
            2. methods addTodoItem 接收到todo unshift进todoList并深度监听存储
            3. watch深度监听 todoList 并 localStorage 存储todoList
               watch:{
               todoList :{
                   deep:true,
                   handler(value){
                       localStorage.setItem('person',JSON.stringify(obj)); 
                   }
                }
  • list 组件
1.列表渲染 props (包含item组件 渲染列表)
    a) 引入item组件,v-for循环item
    b) 数据从App里引入 props引入数据
  • item 组件
【子传父方法2:全局事件总线】(勾选 删除 编辑 confirm forEach )
    1.勾选:全局事件总线(子传父方法2)
        逻辑:触发change事件取id 回传给App 取反对应的id
        1.@change 获取到id回传 this.$bus.$emit('',id)
        2.checkTodoItem(id){forEach}循环取反对应的
        3.mounted():父绑定 this.$bus.$on('checkTodoItem', this.checkTodoItem)
        4.beforeDestroy():this.$bus.$off('checkTodoItem')
            
    2.删除:全局事件总线(子传父方法2)
        逻辑:button绑定handelDelete获取到item的id传给App在那过滤组成新数组
        1.mounted():父绑定 this.$bus.$on('delTodoItem', this.delTodoItem)
        2.beforeDestroy():this.$bus.$off('delTodoItem')
        3.this.$bus.$emit('delTodoItem',id) 子传父 加个confirm确认是否删除
        4.this.delTodoItem 父执行回调处理逻辑过滤出新数组保存data
             
        item:
        methods: {
            handelDelete(id) {
                if(confirm('确定删除吗?')){
                    this.$bus.$emit('delTodoItem',id)
                }
            }
        },
        
        App:
             mounted() {
               // 绑定自定义全局事件总线接收item的id删除todo
               this.$bus.$on('delTodoItem', this.delTodoItem)
             },
             beforeDestroy() {
               // 解绑自定义全局事件总线
               this.$bus.$off('delTodoItem')
             }
    3.编辑(原数据新增一个属性并且修改值响应式用Vue.$set + setTimeout/$nectTick + 子传父方法2)
        逻辑:点击变成input框,编辑完成显示渲染 
              编辑:handlerEdit 修改input显示并获取焦点&隐藏
              失去焦点:handlerBlur 真正执行编辑的回调函数跟App通信
        1.绑定handelEdit在button上 
            - 1.定义一个input v-show变输入框并获取焦点
                a. 定义ref="inputTitle"
                b. 拿到元素调用focus()获取焦点 tihs.$refs.inputTitle.focus()
                c. 由于vue会等整段代码都执行完了才重新解析模板,所以此时获取不到input
                    1) setTimeout(()=>{包一个定时器focus()},200)
                    2) this.$nextTick(function(){this.$refs.input.focus()}) 
                       指定的回调会在dom更新完毕之后执行
            - 2.判断身上有无edit属性,若无则新增,有则仅修改edit状态为true
                a. if(todo.hasOwnProperty('isEdit')){todo.idEdit=true}
                b. 否则 this.$set(todoList,'edit',true)
               
        2.绑定handlerBlur事件在input上
            逻辑:localStorage让他保持编辑状态了一进来就会显示input所以要修改这个bug
            - 1.将按钮改为确认
            - 2.绑定@blur事件 handleBulr(todo) input
                a. 失去焦点的时候变为正常渲染 
                b. todo.isEdit=false // 修改状态 这里不就直接改props了吗??
            - 3.传递id与修改后的title给App this.$bus.$emit('editTodoList',id)
        3.updateTodo(id,title){} App里真实执行回调绑定与解绑
            - 1.找到对应的id 修改title
        5.细节问题:handlerEdit handleBulr
            - 1.handlerEdit点击编辑按钮的时候 获取焦点
                
            - 2.handleBulr失去焦点的时候 要判空 再传数据 return alert 输入不能为空
  • footer 组件
(全选 勾选的数据 清除勾选 cpmputed reduce 子传父第3/4种方法:组件自定义事件)
1.数量判断:
    1.已完成 computed + reduce 计算
    2.全部 computed 计算todoList长度
        <span>已完成{{ doneTotal }}</span> / 全部{{ allTotal }}
        computed: {
            allTotal(){
                return this.todoList.length
            },  
            doneTotal() {
                return this.todoList.reduce((pre, todo) => pre + (todo.done ? 1 : 0), 0)
            }
        }
        
===============================================================================
【子传父方法3:组件自定义事件】 @自定义事件="回调函数名字" this.$emit('回调函数',实参)
2.全选 
    1. <input type="checkbox" v-model="isAll"/> 
    2. computed 计算 isAll : { get() { return 已完成数跟总数是否相同且总数非0 0的时候就没必要勾上全选了}} }
    3. computed 计算 isAll : { set() { return 当isAll被修改的时候 input类型是check 所以isAll的值就是chenked为true或者false 那么被修改的时候就调用 this.$emit('checkAllTodo',value) 将value传过去}}
    4. App那使用组件自定义事件 Methods回调"checkAllTodo"接收value 是true还是false 选择用forEach遍历 将done改为对应的全true或者全false
    5. 自定义事件在标签里:<MyFooter :todos="todos" @checkAllTodo="checkAllTodo" @clearAllTodo="clearAllTodo"/>
    6. 一样可以解绑this.$off('clearAllTodo'); // 在哪儿解绑?
        
    代码片段:
        App:
        <MyFooter :todos="todos" @checkAllTodo="checkAllTodo"  @clearAllTodo="clearAllTodo"/>
        Footer:
            isAll:{
                //全选框是否勾选
                get(){
                    return this.doneTotal === this.total && this.total > 0
                },
                //isAll被修改时set被调用
                set(value){
                    // this.checkAllTodo(value)
                    this.$emit('checkAllTodo',value)
            }
        }
            
===============================================================================
【子传父方法4:组件自定义事件】 this.$ref.Footer.$on() this.$emit() this.$off()
3.清除已完成任务 
    a) 自定义事件在标签里:<MyFooter :todos="todos" @checkAllTodo="checkAllTodo" @clearAllTodo="clearAllTodo"/>
    b) 同上全选/反全选 clearAllTodo 将所有todo为done的移除 用filter剩下done为false的
    c) 换一种写法 用 $ref this.$refs.Footer.$on('clearAllTodo',this.clearAllTodo); // 获取到Footer的dom元素
    d) 优点:可以等几秒再绑定自定义事件 写在mounted里面
    e) 触发:同上 this.$emit('clearAllTodo',id,b,c,d)
    f) App里接收的回调函数 clearAllTodo(id,...params) 这样bcd会以[b,c,d]的形式返回接收到
 ===============================================================================  
【子传父方法5:消息订阅与发布】 
    npm i pubsub.js
    import pubsub from 'pubsub-js';
    monuten():this.pubId = pubsub.subscribe('deleteTodo',this.deleteTodo)
    beforeDestroy:pubsub.unsubscribe(this.pubId)
12.$nextTick(function(){})
作用:在下一次dom更新结束后执行指定的回调
啥时候用:当数据改变后,要dom更新之后再进行某些操作,就在nextTick指定回调种执行,参考11 编辑案例 input 点击编辑 修改edit为true之后 要focus()
也可以定时器,定时器就算时间到了
13.过渡动画