Vue组件化编码以及Vuex入门 | 青训营笔记

41 阅读6分钟

这是我参与「第四届青训营 」笔记创作活动的第6天

  • 组件:实现局部功能效果的代码集合(包括html,css,js,多媒体等)

  • 作用:复用编码、简化项目代码、提高运行效率

  • 名词解释:

    • 非单文件组件:一个文件中有多个组件
    • 单文件组件:一个文件中只有一个组件

基本使用(非单文件组件)

  • 创建组件:

    const comp = Vue.extend({
        template:`
            template here
        `
        name: ''
        data(){
            return {something};
        }
        methods:{
            
        }
        components:{//嵌套的组件
            
        }
    })
    //简写
    const comp = {
        template:``
        .........
    }
    
    • 不能写el,管理哪一个标签最终都会由vm决定
    • data必需写成函数,写成对象会报错(原因:防止复用时产生相互引用)
    • 在指定了name后,不论注册时使用什么名字,开发者工具均会显示此处定义的name
  • 注册组件:

    //方法一(局部注册)
    new Vue({
        components:{
            name1: extend1
            name2: extend2
        }
    })
    //方法二(全局注册)
    Vue.component('name',extend)
    
    • 当name和extend名字相同时,可简写为一个extend
  • 使用组件

    <comp_name></comp_name>
    
    • 会将这个以组件名为标签名的特殊标签会被替换为组件内容
  • 注意事项:

    • 多个单词组件名

      • 可以使用xx-xx-xx的格式,但注意在注册时需要用'xx-xx-xx'
      • 可以使用例如MySchool格式,但必需有Vue脚手架环境的支持
    • 组件名不能跟原生标签名作为组件名

  • VueComponent:

    • 组件本质是一个由Vue.extent生成的名为VueComponent的构造函数
    • 在html中写<comp_name><\comp_name>时Vue创建对应的实例对象(相当于执行new VueComponent(options)
    • 每次调用Vue.extend返回的都是一个全新的VueComponent构造函数(不同组件互不干扰)
    • this指向:组件中data、method、watch、computed中的函数调用this均指向VueComponent实例对象
  • Vue和VueComponent的关系:Vue.prototype === VueComponent.prototype.__proto__

    • 作用:Vue的组件可以访问到Vue的原型对象

单文件组件

  • 文件命名:xxx-xxx.vue格式或XxxXxx.vue格式

  • 组件文件内容基本结构:

    <template>
        <!--html-->
    </template><script>
        export default Vue.extend({
            name: ''
            data(){ }
            
        })
        //可简写为
        export default {
            
        }
    </script><style>
        /*css*/
    </style>
    
    • script内需要暴露vue组件供引入
  • 一般使用App组件汇总所有组件

    • 作为vm唯一的子组件
    • 其它所有组件均为App的后代
  • 引入组件

    import name from path
    

    例如

    import header from './header.vue'
    

脚手架入门

常用指令

  • 创建项目(先切换到创建项目文件夹的父文件夹)

    vue create project_name
    
  • 运行项目(先进入项目根文件夹)

    vue run serve
    

脚手架结构

  • 文件结构

    ┣━━.gitignore: git配置文件
    ┣━━ babel.config.js: babel的配置文件
    ┣━━ package-lock.json: 包版本控制文件
    ┣━━ package.json: 包的说明书
    ┣━━ node_module
    ┣━━ public
             favicon.ico: 网页的页签图标
             index.html: 主页面
    ┗━━ src
               main.js: 整个项目的入口文件
               App.vue: 所有组件的父组件
               assets: 一般用于存放静态资源(图片、视频等)
               components: 放置除app以外的所有组件
    
  • 由于脚手架中使用的vue为vue.runtime.xxx.js,并非完整版的js(缺失模板解析器),所以在main.js中的vm不能使用template属性,解决方法为使用render

    new Vue({
        render: h => h(App)//引入App组件
    })
    

更改脚手架结构

  • 在根目录下创建vue.config.js
  • vue.config.js的写法参考官网文档

ref属性

  • 功能:可以通过ref获取元素

  • 使用:

    • 给标签加上ref属性

      <p ref="aref"></p>
      
    • 在script中获取元素

      this.$refs.aref
      
  • 注意:既可以用ref获取传统html标签,也可以获取子组件标签

    • html标签会获取真实DOM元素,子组件会获得VueComponent实例

props配置项

  • 功能:接受外部传入组件的数据

  • 语法:(以Jack传入name属性,18传入age为例)

    • 传递数据:

      <h1 name="Jack" :age="18"></h1>
      
      • 若将:age改为age则会导致传入的18为字符串
    • 接受:在组件的script内

      • 简单接受:

        prop: ['name','age']
        
      • 限制传入数据类型

        prop:{
            name: String,
            age: Number
        }
        
      • 限制类型和必要性,设置初始值

        prop: {
            name: {
                type: String,
                required: true//必需传入,否则报错
            }
            age: {
                type: Number,
                default: 18//如果未传入,默认值为18
            }
        }
        
  • 注意事项:

    • prop为只读元素,不可修改

    • 如果需要对传入参数进行修改,可以将prop内的值复制进入data中,再操作data内的元素

      data(){
          return {aage:this.age}
      }
      prop: ['age']
      

mixin混入

  • 功能:多个组件共用的配置进行提取形成一个混入对象

  • 语法:

    • 定义混入:把共用配置写入一个js文件(此处用mixin.js)(也可直接写作一个对象)

      const mixin1 = {
          data(){},
          methods:{}
      }
      const mixin2 = {
          data(){},
          methods:{}
      }
      
    • 导入混入对象(若不在同一个文件内)

      import {mixin1,mixin2,...} from path/mixin.js
      
    • 使用混入

      • 全局使用(所有Vue实例及组件实例均可使用)

        Vue.mixin(mixin1)
        
      • 局部混入(当前实例可使用):再当前实例配置项中加上mixin配置

        mixin:['mixin1','mixin2']
        

插件

  • 功能:可以增强Vue

  • 写法:

    • 单独将插件的内容写入js(此处写入plugin.js

      export default {
          install(Vue){
              Vue.filter('',func)//定义全局过滤器
              Vue.directive('',{})//定义全局指令
              Vue.mixin('',{})//定义全局混入
          }
      }
      
    • 调用插件

      import plugins from 'path.plugin.js'
      Vue.use(plugins)
      

scope样式

  • 功能:限制样式只在当前组件生效

  • 语法:在.vue

    <script scope>
    </script>
    

不同组件之间的信息传递

  • 父亲给儿子传数据:prop

  • 儿子给父亲传数据

    • 父亲给儿子传一个方法

    • 儿子调用该方法时传入参数,由于是父亲执行该方法,即可实现父亲操作该数据

      this.xxmethod()
      
      • 此处的this.xxmethod为父亲传入的方法
  • 注意事项:

    • v-model不要绑定props传入的值

      • 若该值为对象,修改对象中的值不会直接报错,但不推荐这样的做法

nextTick

  • 功能:等待下一次DOM更新完毕后执行回调函数

  • 使用:

    vc.$nextTick(function(){
        
    })
    

组件化编码流程

  1. 拆分静态组件:按功能点拆分静态组件

  2. 实现动态组件:重点在于数据的存放位置

    • 若只有该组件使用该数据,则存放在该组件内
    • 若有多个组件使用同一个数据,则存放在他们的LCA上
  3. 实现交互功能:通过事件绑定

浏览器本地存储

  • 存储上限:5MB左右

  • 存储途径:

    • window.localStorage: 本地存储,浏览器关闭不清除,手动删除才会清空
    • window.sessionStorage: 浏览器关闭后会清楚
  • 相关方法

    • 存储

      xxxStorage.setItem('key','value')
      
    • 读取

      xxxStorage.getItem('key')
      
    • 删除

      xxxStorage.removeItem('key')
      
    • 清空

      xxxStorage.clear()
      
  • tips: 通过JSON.stringify(string)将数据转成可读的字符串

    • 对应的读取函数为JSON.parse(string)把数据转换为其它形式(数字、对象等)

自定义事件

  • 功能:可以通过子组件传参实现子组件到父组件的通信

  • 原理:

    • 事件在子组件中触发,触发之后子组件传参
    • 父组件内有对应事件的回调函数,能够接收子组件传递的参数
  • 语法:

    • 绑定事件

      <vc v-on:事件名="xxx"></xx>
      
    • 触发事件(在vc.vue的script中)

      this.$emit('事件名')
      
      • 触发事件时可以添加多个参数,除了第一个事件名外,其余参数会作为事件回调函数的参数
    • 解绑事件

      this.$off('事件名')
      this.$off(['事件名1','事件名2'])//解绑多个事件
      this.$off()//解绑所有事件
      
  • 其它写法:

    • 通过ref属性实现绑定

      <vc ref="aref"></vc><script>
          export default {
              fun(){
                  this.$ref.aref.$on('事件名',this.xxx)
              }
          }
      </script>
      
      • 此处如果直接简写为

        this.$ref.aref.$on('事件名',function{})
        

        此时该回调函数内的this指向触发事件的实例对象(一般是该段代码所在元素的子组件)

    • 在回调函数中,使用数组接受多个参数

      func(x,y,...a){//把第3个参数起后面所有参数存在数组a中
          
      }
      
  • 注意事项:

    • 给组件绑定的事件默认会被当做自定义事件

      <vc @click=''></vc>
      
      • 此时click会被处理成自定义函数名
    • 若要给组件绑定原生事件,应使用.native修饰

      <vc @click.native=""></vc>
      

全局事件总线

  • 功能:任意组件间的通信

  • 原理:

    • 发送至总线:将需要发送数据的组件触发的自定义事件,通过参数传数据到总线
    • 从总线接收:对总线绑定绑定事件,接受发送方发送的数据
  • 使用全局事件总线

    • 安装全局事件总线

      new Vue{
          ...
          beforeCreate(){
              Vue.prototype.$bus = this;//将全局事件总线设置为vm自身
          }
      }
      
    • 接收数据(一般直接在mounted里面绑定)

      mounted(){
          this.$bus.$on('xxx',func)//传入参数执行回调函数func就得到了参数形式的数据
      }
      
    • 发送数据

      this.$bus.$emit('xxx',data1,data2,...)
      
  • 注意:最好在组件的beforeDestroy中解绑当前组件所用到的事件$off

消息订阅

  • 使用的库:pubsub.js

    • 该库可以在任意框架下实现消息订阅

    • 安装方法

      npm install pubsub-js
      
  • 引入库

    import pubsub from 'pubsub-js'
    
  • 发布消息

    pubsub,publish('msgName',data1,data2,...)
    
  • 接收消息

    pubsub.subscribe('msgName',function(){})
    
    • 此时回调函数接受到的参数是发布者传递的所有参数,包括消息名
  • 销毁消息订阅(最好在beforeDestroy里销毁所有订阅)

    pubsub.unsubscribe(pid)
    

Vuex

  • 官方文档:vuex.vuejs.org/zh/

  • 安装:(注意:对vue2只能安装vuex3及其以下的版本)

    npm install vuex@3
    
  • 引入

    import Vuex from 'vuex'
    
  • 使用步骤:

    • 在/src/store里创建index.js,并在其中配置actions、mutations、state,创建并暴露store(注意子newVuex之前要先引入组件Vue.use),index.js基本结构如下

      import Vuex from 'vuex'
      import Vue from 'vue'
      Vue.use(vuex)
      ​
      const actions={
          
      }
      const mutations={
          
      }
      const state={
          
      }
      ​
      const store = new Vuex.Store({
          actions,
          mutations,
          state,
      })
      
    • 组件调用dispatch执行action内的方法

      this.$store.dispatch('actionfunc',arg1,arg2)
      
      • 若是方法非常简单,可以跳过action直接commit

        this.$store.commit('mutafunc',arg1,arg2)
        
    • 配置actions:每个action内的方法都要调用commit

      const actions={
          add(context,value){
              context.commit('ADD',value)
          }
      }
      
      • context里面包含上下文的很多重要信息,包括commit、state、dispatch等
    • 配置mutations:用于操作state内的数据

      • 只有mutations内的操作才会被开发者工具捕获

getter

  • 可以在定义Vuex实例处添加配置getter

  • 类似与vuex的计算属性,可以利用state里面的属性计算出自己的值

  • 用法:

    • 创建(在index.js内)

      const getters = {
          comp(state){
              
          }
      }
      export default new Vuex.store({
          ...
          getters
      })
      
      • 能拿到一个参数为state
    • 读取数据

      $store.getters.comp
      

map映射实现简写

mapState和mapGetters

  • 功能:辅助生成计算属性,避免x.$store.state.xxxx.$store.getters.xxx这种复杂的写法

  • 映射对象

    • mapState映射state内的数据
    • mapGetters映射getters内的数据
  • 写法:

    computed:{
        ...mapxxx({name1:'sname1',name2:'sname2'})
    }
    
    • 若每组的name和sname相同,可简写为

      computed:{
          ...mapxxx(['name1','name2'])
      }
      

mapMutations和mapActions

  • 功能:辅助生成方法

    • mapMutations帮助生成与mutations对话的方法,即含有$store.commit的方法
    • mapActions帮助生成与actions对话的方法,即含有$store.dispatch的方法
  • 写法与上面两个map类似,不过定义在methods内

  • 注意:如果生成的方法需要传参,一定要在绑定事件时传参(<button @xxx='func(a,b)'><\button>),如果不传参的话函数收到的将是事件对象event

模块化Vuex

  • 将不同功能模块所共享的数据分离

  • 写法:

    • 在index.js中创建模块(或在其它js文件中创建之后import进入index.js)

      const aAbout={
          namespace: true
          const actions={},
          const mutations={},
          const state={},
          const getters={},
      }
      const bAbout={
          const actions={},
          const mutations={},
          const state={},
          const getters={},
      }
      
      • namespace为可选配置,默认为false,设置为true后才能使用命名空间
    • 在index.js的Vuex中引入模块

      export default new Vue.store({
          modules:{
              aAbout,
              bAbout,
          }
      })
      
    • 组件内读取数据(在开启命名空间后)

      • state

        //直接读取
        this.$store.state.xxAbout.xxx
        //转存在data内
        ...mapState('xxAbout',['xxx'])
        
      • getters

        //直接读取
        this.$store.getters['xxAbout/xxx']
        //转存在comuted内
        ...mapGetters('xxAbout',['xxx'])
        
      • 调用dispatch

        //直接调用
        this.$store.dispatch('xxAbout/xxx',data)
        //转存在methods内
        ...mapActions('xxAbout',['xxx'])
        
      • 调用commit

        //直接调用
        this.$store.commit('xxAbout/xxx',data)
        //转存在methods内
        ...mapMutations('xxAbout',['xxx'])
        

    \