vue 组件化指导(持续完善ing)

291 阅读3分钟

有关vue双向数据绑定实现原理请移步juejin.cn/post/696760…

1. vue 基础部分

  • 1.1 组件模版
<template>
  <div class="Name">
    <div ref="msgDiv">这是一个组件模版</div>
    <ChildCompnent v-if="ifRenderChildCompnent" ref="childCompnent" :visible.sync="childCompnentVisible"  @hook:mounted="onChildCompnentMounted" />
  </div>
</template>
<script>
export default {
  name: 'String',
  components: {}, // {组件1,组件2},所导入组件名
  props: {// 组件父组件传递值,推荐用对象形式
    yearlimit: {
      type: Array, // 注意若类型为Object||Array,default的值必须用工厂函数返回
      default: function() {
        return []
        // [min,max]
      }
    }
  },
  data() {
    return {
      ifRenderChildCompnent: true,
      childCompnentVisible: false, // 子组件   this.$emit('update:childCompnentVisible',value)
      dataobj: { key: 'value' }
    }
  }, // 组件数据源
  beforeCreate() {
    // alert("创建vue实例前")
  },
  created() {
    // alert("实例创建完成")
  },
  beforeMount() {
    // alert("虚拟dom开始挂载到实际dom树中")
  },
  mounted() {
    // alert("虚拟dom已经挂载到实际dom树中,页面加载完成")
    
    // this.$refs.childCompnent 子组件实例化对象,注意观察这个示例里面的数据结构
    // this.$refs.msgDiv dom节点,注意观察dom节点对象数据结构
   
    // this.$parent 父亲组件实例化对象
  },
  beforeUpdate() {
    // alert("更新前")
  },
  updated() {
    // alert("更新完成")
  },
  beforeDestroy() {
    // alert("销毁前")
  },
  destroyed() {
    // alert("销毁完成")
  },
  // 以上8个为vue的一个生命周期
  activated() {
    // 在vue对象存活的情况下,进入当前存在activated()函数的页面时,一进入页面就触发;可用于初始化页面数据等,与keepAlive对应
  },
  beforeRouteEnter(to, from, next) {
    // 进入路由前
    console.log(this, 'beforeRouteUpdate') // undefined
    next(vm => {
      // 当钩子执行前,组件实例还没被创建
      // vm 就是当前组件的实例相当于上面的 this,所以在 next 方法里你就可以把 vm 当 this 来用了。
      console.log(vm) // 当前组件的实例
    })
  },
  beforeRouteUpdate(to, from, next) {
    // 在当前路由改变,但是该组件被复用时调用
    // 对于一个带有动态参数的路径 /good/:id,在 /good/1 和 /good/2 之间跳转的时候,
    // 由于会渲染同样的good组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
    // 可以访问组件实例 `this`
    console.log(this, 'beforeRouteUpdate') // 当前组件实例
    next()
  },
  beforeRouteLeave(to, from, next) {
    // 导航离开该组件的对应路由时调用
    // 可以访问组件实例 `this`
    console.log(this, 'beforeRouteLeave') // 当前组件实例
    next()
  },
  computed: { // 计算属性
    key() { // 这里的key相当于date里面的数据源,不能在data里面重复定义
      return 'a newValue by calculating and refining known properties in data'
      // return a value(必须)
    }
  },
  filters: { // 过滤器,对一些绑定值进行过滤筛选,比如只允许输入数字 v-model=”value| filterDigital”
    filterName(value) {
      return 'a newValue by filter the value'
    }
  },
  methods: {
    reRenderChildCompnent() { // 子组件 销毁重载
      this.ifRenderChildCompnent =false
      this.$nextTick(()=>{
        this.ifRenderChildCompnent =true
      })
    },
    methodName(arg) {
      // 要执行的操作
    },
    onChildCompnentMounted() {
       setTimeout(()=>{
          console.log(this)
          debugger
          // 浏览器里再次console.log(this)
          // 注意理解箭头函数里面的this指的是 运行环境上下文 的this
       },10)
      // 子组件mounted回调
    }
  },
  watch: {
    dataobj: {
      handler() {
        // 要执行的操作
      },
      deep: true, // 是否深度监听,意为对象dataobj内部任何属性发生变化则全局重新渲染
      immediate: true // 代表在watch里声明了dataobj这个方法之后立即先去执行handler方法
    }
  }
}
</script>
<style lang="scss">
</style>
  • 1.3 vue 指令
<template>
  <div class="hello">
    <!-- v-bind:attrname="attrnamevalue"-->
    <!-- {{}} -->
    <h1 class="h1class">
        <span>
             sass测试
        </span>
    </h1>
    <div v-show="showtype">
      <h1 :class="{ active: isActive, 'text-danger': hasError }" :attrname="attrnamevalue">{{ msg }}</h1>
      <h1 v-bind="{attrname1:(attrnamevalue1=='attr1'?'0':'1')?'a':b,attrname2:attrnamevalue2}">多个属性绑定案例</h1>
      <h1 :attrname="attrnamevalue">这是attrname</h1>
      <h1>{{text}}</h1>
      <h1 style="color: #ae00ff">{{computedh1value2hastag}}</h1>
      <h1>{{h1value2hastag?'这是一个双大括号文本text1':'这是一个双大括号文本text2'}}</h1>
      <h1>{{h1value2hastag?'<span>这是一个双大括号文本text</span>':'这是一个双大括号文本text'}}</h1>
      <br>
      <!-- v-text="text" -->
      <div v-text="text"></div>
      <br>
      <!-- v-html="html" -->
      <div style="color:#f00" v-html="text"></div>
      <div style="color:#f00" v-html="h1value2hastag?'<span>这是一个绑定标签文本text</span>':'这是纯文本text'"></div>
      <br>
      <!--  v-model="value" -->
      <Input v-model="value" readonly placeholder="Enter something..." style="width: 300px" />
      <Input v-model="radominputvalue" placeholder="Enter something..." style="width: 300px" />
      <br>
      <br>
      <input id="ceshiid" type="text" readonly value="绑定不同事件类型对象语法" v-on="{blur:inputblur,focus:inputfocus}" />
      <input type="text" readonly value="绑定不同事件类型分开绑定" @blur="inputblur" @focus="inputfocus" />
      <br>
      <br>
      <!-- v-on:click="buttonalert(123)"-->
      <Button type="primary" v-on:click="buttonalert1(123)">getinputvalue</Button>
      <Button type="primary" v-on:click="buttonalert1(123);buttonalert2(234)">绑定不同方法</Button>
      <Button type="primary" v-on:click="buttonalert3(123);buttonalert3(234)">重复绑定某个方法</Button>
      <br>
      <!-- v-if="isshowdiv1" -->
      <div class="div1" style="height:100px;background:#0f0" v-if="isshowdiv1"></div>
      <!-- v-show="isshowdiv2" -->
      <div class="div2" style="height:100px;background:#00f" v-show="isshowdiv2"></div>
      <br>
      <!-- v-for="(item,index) in acdatelist" :key="index" -->
      <ul>
        <li v-for="(item,index) in datelist" :key="index" :attr="index" :ref="'li'+index" class="fn-inline" @click="setlivalue(index)">
          datelist{{index}}的值为{{item}}
        </li>
      </ul>
      <Button type="info" v-on:click="getrefs">getrefs</Button>
      <!-- 绑定Class -->
      <h1 :class="isactive ? 'active fn-inline' : 'fn-inline'">以条件语法绑定一个属性值</h1>
      <h1 :class="{class1:isaddclass1,class2:isaddclass2}">对象语法绑定一个属性的多个值</h1>
      <br>
      <!--    深拷贝解决监听层级比较深的数据的改变前后值变化 -->
      <ul>
        <li 
            v-for="(item,index) in acdatelist" 
            :key="index"
            :style="{
                '--background': colorBgConfig[index].color + '15',
                '--background-active': colorBgConfig[index].color,
                '--background-hover': colorBgConfig[index].color,
            }" 
            :class="activeLiIndex===index?'active':''"  
            class="list fn-inline"  
            @click="setlivalue(index)">
          datelist{{index}}的值为{{item | valuetofixed(1) | unitvalue}}
        </li>
      </ul>
      <!-- 计算属性 -->
      <Input v-model="inputcountvalue1" placeholder="Enter something..." style="width: 300px" />
      <span >*</span>
      <Input v-model="inputcountvalue2" placeholder="Enter something..." style="width: 300px" />
      <span >=</span>
      <Input v-model="inputcountedvalue " placeholder="Enter something..." style="width: 300px" />
      <br>
      <br>
      <br>
      <!-- 路由 -->
      <!-- <Button type="success" :click="gorouter('Component1')">gorouterComponent1</Button> -->
    </div>
    <br>
    <br>

    <Button type="success" @click="gorouter('Component2')">gorouterComponent2</Button>
    <Button type="success" @click="gorouter('Component1')">gorouterComponent1</Button>
    <Button type="success" @click="gorouter('Table')">gorouterTable</Button>
    <br>
    <br>
  </div>

</template>

<script>
export default {
  name: 'hello',
  components: { 
  },
  data() {
    return {
      msg: "这是一个vue project",
       colorBgConfig: [
        {
          color: '#2D8DFD'
        }, 
        {
          color: '#FF652E'
        }, 
        {
          color: '#997BFF'
        }, 
        {
          color: '#01A883'
        }, {
          color: '#FFA522'
        }
      ],

      isActive: true,
      hasError: false,
      datelist: ["01.234", "12.345", "13.456", "14.567", "15"],
      activeLiIndex: -1,
      text: "<span>这是一个文本text</span>",
      h1value2hastag: true,
      attrnamevalue: "zheshiyigeH1标签",
      isshowdiv1: false,
      isshowdiv2: false,
      radominputvalue: Math.ceil(Math.random() * 100),
      isactive: true,
      isaddclass1: true,
      isaddclass2: false,
      attrnamevalue1: "attr1",
      attrnamevalue2: "attrvalue2",
      inputcountvalue1: "",
      inputcountvalue2: "",
      showtype: true,
    }
  },
  computed: {
    value() {
      return "inputvalue" + Math.ceil(Math.random() * 100)
    },
    inputcountedvalue() {
      return this.inputcountvalue1 * this.inputcountvalue2
    },
    acdatelist() {
      //return Object.assign([], this.datelist);
      return JSON.parse(JSON.stringify(this.datelist))
    },
    computedh1value2hastag(arg) {
      return this.h1value2hastag ? '这是一个双大括号文本text1' : '这是一个双大括号文本text2';
    }
  },
  filters: {
    valuetofixed(count, limit) {
      return Math.floor(count * (Math.pow(10, limit))) / (Math.pow(10, limit))
    },
    unitvalue(value) {
      return value + "¥"
    },
  },
  methods: {
    buttonalert1() {
      // console.log("触发button元素的alert方法", "input框的值为", this.radominputvalue)
      alert("触发button元素的alert1方法,input框的值为:" + this.radominputvalue)
    },
    buttonalert2() {
      // console.log("触发button元素的alert方法", "input框的值为", this.radominputvalue)
      alert("触发button元素的alert2方法,input框的值为:" + this.radominputvalue)
    },
    buttonalert3(arg) {
      alert("触发button元素的alert3方法,参数为:" + arg)
    },
    randomValue: function() {
      setTimeout(()=>{
        this.radominputvalue = Math.ceil(Math.random() * 100);
      }, 2000)
    },
    setlivalue(index) {
      this.activeLiIndex = index
      this.$set(self.datelist, index, "1234.2345")
    },
    inputblur() {
      console.log("blur")
    },
    inputfocus() {
      console.log("focus")
    },
    getrefs() {
      console.log(this.$refs)
      console.log(this.$refs.li1[0])
    },
    gorouter(rourername) {
      //this.showtype = false;
      //this.$router.push(rourername);
      var curroute = { name: rourername }
      Object.assign(curroute, { id: 10 });
      this.$router.push(curroute);
    },


  },
  watch: {
    radominputvalue(newValue, oldValue) {
      console.log("oldValue:", oldValue, "newValue:", newValue)

    },
    // datelist: {
    //   handler(newValue, oldValue) {
    //     //console.log("oldValue:", oldValue, "newValue:", newValue)
    //   },
    //   immediate: true,
    //   deep: true
    // },
    acdatelist: {
      handler(newValue, oldValue) {
        //console.log(oldValue, newValue)
        console.log("oldValue:", oldValue, "newValue:", newValue)
      }
      //   immediate: true,
      //   deep: true
      // }
    },
  },
  mounted() {
    this.randomValue()
  },
}

</script>
<!-- Add "scoped" attribute to limit CSS to this component only,only Demo-->
<style scoped lang="scss">
h1,
h2 {
  font-weight: normal;
  font-size: 14px;

}

.h1class{
  span{
     color:#f00   
  }
}

input {
  display: inline-block;
  width: 250px;
  height: 32px;
  line-height: 1.5;
  padding: 4px 7px;
  font-size: 12px;
  border: 1px solid #dcdee2;
  border-radius: 4px;
  color: #515a6e;
  background-color: #1972be;
  background-image: none;
  position: relative;
  cursor: text;
  transition: border .2s ease-in-out, background .2s ease-in-out, box-shadow .2s ease-in-out;
}

ul {
  list-style-type: none;
  padding: 0;
}

li {
  display: block;
  margin: 0 10px;
  color: #5800f7;
  font-size: 14px;
}

a {
  user-select: none;
  padding: 5px 15px 6px;
  font-size: 12px;
  border-radius: 4px;
  background-color: #19be6b;
  border-color: #19be6b;
  color: #fff;
  font-size: 14px;
}
ul li:hover{
  color: #f00;
}
.list{
  li.active{
    background:var(--background-active);
  }
  li:hover{
    background:var(--background-hover);
  }
}
</style>

  • 1.3 一个简单的弹框
<template>
  <div>
    <vxe-modal
      ref="xeModal"
      v-model="visible"
      title="title"
      :position="position"
      transfer
      width="width"
      destroy-on-close
      :show-footer="true"
      :confirm-button-text="confirmButtonText"
      :cancel-button-text="cancelButtonText"
      @cancel="onCancelClick"
      @close="onCancelClick"
      @confirm="onConfirmClick"
    >
      <div v-if="$slots.default">
        <slot></slot>
      </div>
      <div v-if="!$slots.default">
        <vxe-textarea
          v-model="content"
          :autosize="autosize"
          :placeholder="placeholder"
        />
      </div>
    </vxe-modal>
  </div>
</template>

<script>
export default {
  name: 'Confirm',
  components: {},
  props: {
    visible: {
      type: Boolean,
      default: false
    }
  },
  data() {
    return {
      position: {
        top: '20%'
      },
      width: '40%',
      title: '确认弹框',
      confirmButtonText: '确定',
      cancelButtonText: '取消',
      autosize: {
        minRows: 5,
        maxRows: 10
      },
      placeholder: '请输入意见!',
      content: ''
    }
  },
  methods: {
    onCancelClick() {
      this.$emit('onCancel', this.content)
      if (typeof this.cancel === 'function') {
        this.cancel(this)
      }
    },
    onConfirmClick() {
      this.$emit('update:visible', false)
      this.$emit('onConfirmSure', this.content)
      if (typeof this.confirm === 'function') {
        this.confirm(this.content, this)
      }
    }
  },
  mounted() {
  },
  watch: {
    visible: {
      handler(newval) {
      },
      deep: true,
      immediate: true
    }
  }
}
</script>

<style lang='scss'>
</style>

调用示例:
<Confirm>
     :visible.sync="confirmVisible"
     v-if="confirmVisible"
     @onConfirmSure="onConfirmSure"
</Confirm>
  • 1.4 简单的弹框的实例化调用
import Vue from 'vue'
import Confirm from './Confirm.vue'

const isVNode = (node) => {
  return node !== null && typeof node === 'object' && Object.prototype.hasOwnProperty.call(node, 'componentOptions')
}

let id = 'confirm_0'
let ConfirmConstructor = Vue.extend(Confirm)
let instance
let instances = {}
let seed = 1
const Confirm = function (options) {
  if (Vue.prototype.$isServer) return
  options = options || {}
  if (typeof options === 'string') {
    options = {
      content: options
    }
  }
  let userOnClose = options.cancel
  let userOnConfirm = options.confirm
  id = 'confirm_' + seed++
  options.cancel = options.cancel || function () {
    Confirm.closeAndDestroy(id, userOnClose)
  }
  options.confirm = function (content, context) {
    Confirm.confirmAndDestroy(id, userOnConfirm, content, context)
  }
  instance = new ConfirmConstructor({
    data: options
  })
  instance.id = id
  if (isVNode(instance.content)) {
    instance.$refs.xeModal.$slots.default = [instance.content]
    instance.$slots.default = [instance.content]
    instance.content = null
  }
  instance.$mount()
  document.body.appendChild(instance.$el)
  instance.visible = true
  instances[id] = instance
  return instance
}
Confirm.closeAndDestroy = function (cid, userOnClose) {
  if (typeof userOnClose === 'function') {
    userOnClose(instances[cid || id])
  }
  document.body.removeChild(instance.$el)
  instances[cid || id].$destroy()
  delete instances[cid || id]
}
Confirm.confirmAndDestroy = function (cid, userOnConfirm, content, context) {
  if (typeof userOnClose === 'function') {
    userOnConfirm(content, context)
  }
  document.body.removeChild(instance.$el)
  instances[cid || id].$destroy()
  delete instances[cid || id]
}

export default Confirm

调用示例:
this.$Confirm({
   content: '',
   confirm(msg) {
     console.log('confirmMsg', msg)
   },
   cancel() {

   }
})
  • 1.5 keepAlive实现
export default {
  name: 'keep-alive',
  // abstract: true,
  props: {
    include: [String, RegExp, Array],
    exclude: [String, RegExp, Array],
    cacheKeyType: {
      type: [String],
      default() {
        return 'routerName' // componetName
      }
    },
    max: [String, Number]
  },
  created() {
    this.cache = Object.create(null)
    this.keys = []
  },
  destroyed() {
    for (const key in this.cache) {
      this.pruneCacheEntry(this.cache, key, this.keys)
    }
  },
  mounted() {
    this.$watch('include', val => {
      this.pruneCache(this, name => this.matches(val, name))
    })
    this.$watch('exclude', val => {
      this.pruneCache(this, name => !this.matches(val, name))
    })
  },
  methods: {
    remove(arr, item) { // 移除某个元素
      if (arr.length) {
        const index = arr.indexOf(item)
        if (index > -1) {
          return arr.splice(index, 1)
        }
      }
    },
    isRegExp(v) { // 是否正则
      return Object.prototype.toString.call(v).slice(8, -1) === 'RegExp'
    },
    isDef(v) { // 是不是undefined
      return v !== undefined && v !== null
    },
    isAsyncPlaceholder(node) {
      return node.isComment && node.asyncFactory
    },
    getFirstComponentChild(children) {
      const { isDef, isAsyncPlaceholder } = this
      if (Array.isArray(children)) {
        for (let i = 0; i < children.length; i++) {
          const c = children[i]
          if (isDef(c) && (isDef(c.componentOptions) || isAsyncPlaceholder(c))) {
            return c
          }
        }
      }
    },
    getComponentName(opts) {
      return opts && (opts.Ctor.options.name || opts.tag)
    },
    matches(pattern, name) {
      const { isRegExp } = this
      if (Array.isArray(pattern)) {
        return pattern.indexOf(name) > -1
      } else if (typeof pattern === 'string') {
        return pattern.split(',').indexOf(name) > -1
      } else if (isRegExp(pattern)) {
        return pattern.test(name)
      }
      /* istanbul ignore next */
      return false
    },
    pruneCache(keepAliveInstance, filter) {
      const { getComponentName, pruneCacheEntry } = this
      const { cache, keys, _vnode } = keepAliveInstance
      for (const key in cache) {
        const cachedNode = cache[key]
        if (cachedNode) {
          const name = getComponentName(cachedNode.componentOptions)
          if (name && !filter(name)) {
            pruneCacheEntry(cache, key, keys, _vnode)
          }
        }
      }
    },
    pruneCacheEntry(cache, key, keys, current) {
      const { remove } = this
      const cached = cache[key]
      if (cached && (!current || cached.tag !== current.tag)) {
        cached.componentInstance.$destroy()
      }
      cache[key] = null
      remove(keys, key)
    },
    destroy(key) {
      this.pruneCacheEntry(this.cache, key, this.keys)
    },
    update(key) {
      const { cache, keys, pruneCacheEntry } = this
      const slot = this.$slots.default
      const vnode = this.getFirstComponentChild(slot)
      pruneCacheEntry(this.cache, key, this.keys)
      keys.push(key)
      cache[key] = vnode
    }
  },
  render() {
    const { matches, getComponentName, pruneCacheEntry, remove, cache, keys } = this
    const slot = this.$slots.default
    const vnode = this.getFirstComponentChild(slot)
    const componentOptions = vnode && vnode.componentOptions
    if (componentOptions) {
      // check pattern
      const name = this.cacheKeyType === 'routerName' ? this.$route.name : getComponentName(componentOptions)
      const { include, exclude } = this
      if (
        (include && (!name || !matches(include, name))) ||
        (exclude && name && matches(exclude, name))
      ) {
        return vnode
      }
      let ifCache = this.cacheKeyType === 'routerName' ? this.$route.meta.keepAlive : true
      let key = vnode.key == null
        ? componentOptions.Ctor.cid + (componentOptions.tag ? `::${componentOptions.tag}` : '')
        : vnode.key
      if (this.cacheKeyType === 'routerName') {
        key = this.$route.name
      }
      if (ifCache) {
        if (cache[key]) {
          vnode.componentInstance = cache[key].componentInstance
          remove(keys, key)
          keys.push(key)
        } else {
          cache[key] = vnode
          keys.push(key)
          if (this.max && keys.length > parseInt(this.max)) {
            pruneCacheEntry(cache, keys[0], keys, this._vnode)
          }
        }
      }
      vnode.data.keepAlive = true
    }
    return vnode || (slot && slot[0])
  }
}

  • 1.6 递归组件
一种自己调用自己的组件,至于有哪些应用,自己悟吧!
  • 1.7 vueX状态管理 和 模块化
vueX使用说明

1this.$store : 我们可以通过 this.$store 在vue的组件中获取vuex的实例。
2、State : vuex中的数据源,我们可以通过 this.$store.state 获取我们在vuex中声明的全局变量的值。
3、Getter: 相当于vue中的computed , 及 计算属性, 可以用于监听、计算 state中的值的变化
4、Mutation: vuex中去操作数据的方法 (只能同步执行)
5、Action: 用来操作 Mutation 的动作 , 他不能直接去操作数据源,但可以把mutation变为异步的
6、Module: 模块化,当你的应用足够大的时候,你可以把你的vuex分成多个子模块

import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
export default new Vuex.Store({
    // 在state中去声明全局变量,可以通过 this.$store.state 访问
    state: {
        count: 0
    },
    // 在getters中声明state中变量的计算函数,缓存计算后的数据, 通过 this.$store.getters 调用
    getters: {
        // 接受state作为参数,每次 count发生变化时 , 都会被调用
        consoleCount: state => {
            console.log('the state count : ' + state.count);
            return state.count;
        }
    },
    // 只能执行同步方法,不要去执行异步方法 通过 this.$store.commit 方法去调用
    mutations: {
        // 改变state状态的方法,不建议直接通过  
        // this.$store.state.? = ?的方式改变state中的状态
        addCount: state => {
            ++state.count;
        },
        // 自定义改变state初始值的方法,mutations的第一个参数即为state对象,并且可以向mutation传入额外的参数(变量或对象);
        addNumCount: (state, n) => {
            state.count+=n;
        },
    },
    // 借助actions的手去 执行 mutations , 通过  this.$store.dispatch 的方式调用
    // 可以用来执行异步操作,可以跟踪异步数据状态变化
    actions: {
         // 调用 mutation
        addCount: context => {
            context.commit('addCount');
        },
        addNumCount: (context, n) => {
            context.commit('addNumCount', n);
        }
    }
})

我们在代码中分别注册了,state、getters、mutations、actions。
这样我们就可以在任何一个 component中通过this.store.dispatch(′addNumCount′,5);或者 this.$store.dispatch('addCount'); 去触发actions操作来改变state中的值。



应用示例:
vueX定义
import Vue from "vue";
import Vuex from "vuex";

Vue.use(Vuex);

const state = {
  loading: false,
  userInfo: {
    phone: 123456789000000,
    account: "Titans",
  }, //用户信息
  login: false, //是否登录
  shopList:[{
    id: 1,
    name: '兰博基尼',
    price: 10000000
  },{
    id: 2,
    name: '奥迪',
    price: 1000000
  }],
};

const getters = { //实时监听state值的变化(最新状态)
  isloading(state) { //承载变化的login的值.  //.$store.getters.isloading
    return state.loading
  },
  islogin(state) { 
    return state.login
  },
  getuserInfo(state){
  	return state.userInfo
  }
};
const mutations = {
  setloading(state, isshow) { //自定义改变state初始值的方法,这里面的参数除了state之外还可以再传额外的参数(变量或对象);
    state.loading = isshow;
  },
  setlogin(state, islogin) { //this.$store.commit("setlogin", true)
    state.login = islogin;
  },
  setuserInfo(state, userInfoobj){
  	state.userInfo=userInfoobj
  }
};
const actions = {
  asyncsetoading: (context,loadingstatus) => {//this.$store.dispatch("asyncsetoading", false)
    context.commit('setloading',loadingstatus);
  },
};
const modulea = {
  namespaced: true,
  state: {
    usera: "taitan",
    shopList: [{
      id: 1,
      name: '兰博基尼',
      price: 10
    }, {
      id: 2,
      name: '五菱宏光',
      price: 99999
    }],
  },
  mutations: {
    setusera(state,name) {
      state.usera = name
    }
  },
  actions: {
    asyncsetusera:(context, name)=> {
      context.commit("setusera", name)
    }
  },
  getters: {
    getusera(state) {
      return state.modulea.usera
    }

  }
}
export default new Vuex.Store({
  state,
  getters,
  mutations,
  actions,
  modules: { modulea
   }
});

页面调用:
<template>
  <div class="vuexstore">
    <h1>{{ msg }}</h1>
    <br>
    <Button type="primary" @click="goBack">goBack one step</Button>
    <br>
    <br>
    <Button type="primary" @click="getstate">getstate</Button>
    <Button type="primary" @click="setstate">setstate</Button>
    <Button type="primary" @click="setstateasync">setstateasync</Button>
    <Button type="primary" @click="getmapvuex">getmapvuex</Button>
  </div>
</template>
<script>
import { mapGetters, mapMutations, mapState, mapActions } from 'vuex'
export default {
  name: 'Vuexstore',
  components: {

  },
  data() {
    return {
      msg: "以下是一个vue project组件VUEX",
    }
  },
  computed: {
    // 使用对象展开运算符将 getter 混入 computed 对象中
    ...mapGetters([
      'getuserInfo',
      'islogin',
      'isloading'
      // ...
    ]),
    // getuserInfo(){
    //   return this.$store.getters.getuserInfo
    // },
    ...mapState([
      // 映射 this.shopList为 store.state.shopList
      'shopList'
    ]),
    ...mapState('modulea', ['usera'])
  },
  methods: {
    goBack() {
      window.history.length > 1 ?
        this.$router.go(-1) :
        this.$router.push('/')
    },
    getstate() {
       alert(this.getuserInfo)
      alert(this.$store.state.userInfo.phone)
      this.$store.state.userInfo.phone=12344566
      alert(this.$store.state.modulea.usera)
    },
    setstate() {
      this.$store.commit("setlogin", true)
      alert(this.$store.getters.islogin)
      //this.setusera("taitanmodul");
      this.$store.commit('modulea/setusera',"taitanmodul")

      alert(this.$store.state.modulea.usera)
      alert(this.$store.getters['modulea/getusera']) 
      this.setusera("taitanmodulmap");
      alert(this.$store.state.modulea.usera)
    },
    setstateasync() {
      this.$store.dispatch("asyncsetoading", false)
      alert(this.$store.getters.isloading)
      this.$store.dispatch('modulea/asyncsetusera',"taitanmodulceshi")
      alert(this.$store.getters['modulea/getusera']) 
    },
    getmapvuex() {
      console.log(this.getuserInfo);
      console.log(this.shopList);
      this.setlogin("mapfalse")
      console.log(this.$store.getters.islogin);

      this.setuserInfo({
        phone: 19993170150,
        account: "Titans",
      })
      console.log(this.$store.state.userInfo.phone);
      this.asyncsetoading("已经加载")
      console.log(this.$store.getters.isloading);
      console.log(this.usera);

    },
    ...mapMutations('modulea',[
      'setusera', 
    ]),
    ...mapMutations([
      'setlogin', // 将 `this.setlogin(true)` 映射为 `this.$store.commit('setlogin',true)`
      // `mapMutations` 也支持载荷:
      'setuserInfo' // 将 `this.setuserInfo(info)` 映射为 `this.$store.commit('setuserInfo', info)`
    ]),
    ...mapActions({
      asyncsetoading: 'asyncsetoading' // 将 `this.asyncsetoading(false)` 映射为 `this.$store.dispatch("asyncsetoading", false)`
    })

  },
  mounted() {

  },
}
</script>
  • 1.8 可保持复用菜单路由
实现方法
addRoute() And <KeepAlive>

<KeepAlive ref="keepAlive">
  <router-view v-if="$route.meta.keepAlive && ifrouteractive" :key="$route.name" />
</KeepAlive>
<router-view v-if="!$route.meta.keepAlive && ifrouteractive" :key="$route.name" />

this.$refs.keepAlive.destroy(this.$route.name)


路由和组件的常用两种懒加载方式:
1、vue异步组件实现路由懒加载
  component:resolve=>(['需要加载的路由的地址',resolve])
2、es提出的import(推荐使用这种方式)
  const HelloWorld = ()=>import('需要加载的模块地址')
npm run build后会新增路由数量与之加载相匹配的.js文件,在切到相应路由或组件时动态加载这个文件。

// 这两条路由被打包在相同的块中,访问任一路由都会延迟加载该路由组件
const OtherMassivePage = r => require.ensure([], () => r(require('./routes/OtherMassivePage.vue')), 'big-pages')
const WeightLossPage = r => require.ensure([], () => r(require('./routes/WeightLossPage.vue')), 'big-pages')



尤其需要注意,并请思考多次 import 同一个 的全局提升,导入以及导出的提升,有关此请移步
JavaScript大总结es6++(next)部分(持续完善ing)https://juejin.cn/post/6986657782720266248

const data={a:123} // 复杂数据类型
export default data     

A组件导入

import data from 'url/*/*/*/*'       
dada(){                            
  return {
    dataa:dataa
  }
},
methods:{
  fn(){
    this.dataa.a=234
  }
}
B组件也像A组件这么导入和调用,如果A组件先掉用,B组件再掉用,请问B组件导入进来的DataA是A组件修改后的值还是定义导出所写的那个值?
  • 1.9 render函数
vue render函数说明
  render: (h, params) => {
    var vm = this;
    return h('Input', {
      attrs: {
        value: params.row.age,
        readonly: params.row.status == 2 ? false : true,
        class: params.row.status == 2 ? "border ivu-input" : "noborder ivu-input",
      },
      on: {
        input(val) {
          这里用的是iview Input组件,input事件的返回值是改变后的值,若要调用event,需直接调用:var event=window.event;
          若直接用原生的input标签,input事件的返回值是event,改编后的值用event.target.value;
          //值改变时 
          //将渲染后的值重新赋值给单元格值;
          params.row.age = val;
          vm.data8[params.index] = params.row;
        }
      }
    })
  }
  上面是渲染表格的某一列的一个例子,当需要分情况渲染不同元素时,可以如下格式去写:
    render: (h, params) => {
      return h('div', [
        h('Button', {
          style: {
            display: params.row.status == 0 ? "inline-block" : "none",
            marginRight: '5px'
          },
          on: {
            click: () => {
              this.edit(params.index)
            }
          }
        }, 'edit'),
        h('Button', {
          style: {
            display: params.row.status == 2 ? "inline-block" : "none",
            marginRight: '5px'
          },
          on: {
            click: () => {
              this.endedit(params.index)
            }
          }
        }, 'endedit')
      ]);
    }
  }

  h相当于createElement,为一个回调函数,h的第二个参数为一个对象,也可以为一个数组,每个值又为一个对象,当然也可以嵌套多层标签,支持的所有属性如下(以下所有的属性值都支持表达式赋值)
  {
  // 和`v-bind:class`一样的 API
  'class': {
    foo: true,
    bar: false
  },
  // 和`v-bind:style`一样的 API
  style: {
    color: 'red',
    fontSize: '14px'
  },
  // 正常的 HTML 特性
  attrs: {
    id: 'foo'
  },
  // 组件 props
  props: {
    myProp: 'bar'
  },
  // DOM 属性
  domProps: {
    innerHTML: 'baz'
  },
  // 事件监听器基于 `on`
  // 所以不再支持如 `v-on:keyup.enter` 修饰器
  // 需要手动匹配 keyCode。
  on: {
    click: this.clickHandler
  },
  // 仅对于组件,用于监听原生事件,而不是组件内部使用 `vm.$emit` 触发的事件。
  nativeOn: {
    click: this.nativeClickHandler
  },
  // 自定义指令。注意事项:不能对绑定的旧值设值
  // Vue 会为您持续追踪
  directives: [
    {
      name: 'my-custom-directive',
      value: '2',
      expression: '1 + 1',
      arg: 'foo',
      modifiers: {
        bar: true
      }
    }
  ],
  // Scoped slots in the form of
  // { name: props => VNode | Array<VNode> }
  scopedSlots: {
    default: props => createElement('span', props.text)
  },
  // 如果组件是其他组件的子组件,需为插槽指定名称
  slot: 'name-of-slot',
  // 其他特殊顶层属性
  key: 'myKey',
  ref: 'myRef'
}

2 深入组件

  • 2.1 动画 transition
  • 2.2 插槽,slotsslots 与 scopeSlots
  • 2.3 render函数

3 全局api