2.vue

14 篇文章 0 订阅
5 篇文章 0 订阅

Vue2

脚手架安装:

npm i @vue/cli -g

eslint关闭

在vue.config.js中

const { defineConfig } = require('@vue/cli-service')
// 当前项目使用的配置文件
module.exports = defineConfig({
  transpileDependencies: true,
  lintOnSave: false,//关闭eslint 
  configureWebpack: {
    entry: './day6/index.js'
  }
})

入口js设置

// Vue2 是个类
import Vue from 'vue'
import App from './App1.vue'
// .vue文件经过vue-loader处理之后会变成一个对象
console.log(App)
let vm = new Vue({
  // el:'#app'
  render(createElement) {
    return createElement(App)
  }
}).$mount('#app')

1.常用指令

在Vue2中 v-for 和v-if 一起使用v-for的优先级更高

<template>
  <div>
    <h1 v-if="name.length < 4">hello world{{ name }}</h1>
    <h2 v-show="name.length < 4">hello world{{ name }}</h2>
    <div v-html="str"></div>
    <div v-text="str"></div>
    <input type="text" v-model="name">
    <ul>
      <li v-for="(item, index)  in  ary" :key="item">{{ index }}--{{ item }}</li>
      <li v-for="(value, key) of obj">{{ value }}</li>
    </ul>
    <ul v-pre>
      <li>1</li>
      <li>2</li>
      <li>3</li>
      <li>4</li>
      <li>5</li>
      <li>6</li>
      <li>7</li>
      <li>8</li>
      <li>9</li>
      <li>10</li>
      <li>11</li>
      <li>12</li>
      <li>13</li>
      <li>14</li>
      <li>15</li>
      <li>16</li>
      <li>17</li>
      <li>18</li>
    </ul>
  </div>
</template>
<script>
/* 
  Vue中的指令: 就是一些v-xxx的行内属性
  v-model 一般用在表单元素或者组件

  // 不常用的
    v-cloak 为了解决小胡子的显示问题  这个指令vue渲染完成之后会删除这个行内属性;工程化中没有
    v-pre 他是一个优化的指令
    v-html  利用的就是原生的innerHTML; 
    v-text  利用的就是原生的innerText

  
  //常用的
  v-show 
  v-if
  这两者后跟的都会转成一个布尔类型
  v-show 是控制了元素的显示隐藏 有较大的初始加载开销
  v-if 控制的是元素的加载   有较大的切换开销
  v-else-if
  v-else

  v-for 循环展示标签的一个指令  可以用来循环 “数组”  对象 数字 字符串

*/
export default {
  data() {
    // 要求必须是函数返回一个对象的格式: 为了保证组件数据的独立性
    return {
      name: "珠峰",
      str: '<i>666</i>',
      ary: [111, 222, 333, 444],
      obj: {
        q: 1, w: 2, e: 3
      }
    }
  },
  created() {
    console.log(this)
  }
}
</script>

2.事件处理及数据的挂载

虚拟dom vnode  ,在node_modules -> vue - > src -> core -> vdom ->vnode.js

双向数据绑定事靠defineproperty

在node_modules -> vue - > src -> core -> vdom ->index.js 原生

在node_modules -> vue - > src -> core -> instance -> state.js (initState函数)

options是我们组件script里写的配置项,可以拿到props、methods、data,先初始化data,先拿到自己写的在组件data,检测data是不是函数,是函数调用getData(data和vm实例穿进去),不是则直接把data还给他。getData先去返回data.call(vm,vm),拿属性名、props、methods,属性名的长度,先检查有没有报错,

<template>
  <div>
    <input type="text" v-model="name" @keydown.enter="add(5)">
    <input type="text" :value="name" @input="name = $event.target.value">
    <h2 v-for="item in ary" :key="item" class="qqq" :class="{ box: name.length > 3 }">{{ name }}
    </h2>
    <div :style="{ color: 'red' }">666</div>
    <button @click="add(10, $event)">+</button>
  </div>
</template>
<script>
export default {
  name: 'XXX',
  data() {
    // this 当前实例
    return {
      // 这里边的属性 最终都会变成当前实例的私有属性
      name: 100,
      cla: "box",
      ary: [1]
    }
  },
  methods: {
    // methods中的方法 不能写成箭头函数;
    // 里边的函数中的this都是当前实例
    // 这里边的属性 最终都会变成当前实例的私有属性
    add(n, e) {
      // 
      console.log(n, e)
      this.name++
    },
  },
  components: {

  }
}
// @ is an alias to /src
/* 
  key  是用来提升虚拟DOM的diff算法效率的  同层比较
    虚拟DOM: 使用JS对象模拟真实DOM
    {
      tag:'div',
      children:[
        {
          tag:input
        },
        {
          tag:'h2'
        }
      ]
    }

    {
      tag:'div',
      children:[
        {
          tag:input
        },
        {
          tag:'h2'
        },
        {
          tag:'h2'
        }
      ]
    }


  v-bind  专门用来处理行内属性的一个指令  简写 :
    这个指令在处理 class和style专门做过加强

  v-on 专门用来处理事件的一个指令  简写 @  
    事件修饰符 
    @click.stop='fn'  //阻止冒泡
    @click.prevent='fn'  //阻止默认事件
    @click.self = 'fn' // 只有点击自己的时候才会触发 冒泡触发不了这个执行

  v-model的修饰符: v-model.lazy     v-model.number  v-model.trim
    v-model 是 :value 和 @input 结合体
    v-model.lazy  把input事件改成了 change事件
     v-model.number 把input的内容使用parseFloat转成数字, 转成NaN的就不转了
     v-model.trim  去除尾首空格的
  
  v-bind的修饰符  .sync 说组件的时候 在讲
  按键修饰符 
    @keydown.enter="add(5)"  敲回车触发后边的函数
*/
</script>
<style lang="less">
</style>

3.生命周期函数

<template>
  <div>

  </div>
</template>
<script>
// @ is an alias to /src
// Vue在渲染组件的时候 会分成几个节点;会在对应的节点 执行对应的函数 这些函数咱们叫做钩子函数
export default {
  beforeCreate() {
    // 咱们一般不用
    // 获取不到 像data props methods中的属性的
  },
  created() {
    // 这个钩子函数 咱们一般用来发送ajax请求
  },
  beforeMount() {
    // 一般不用
  },
  mounted() {
    // 可以用来做一些需要使用DOM的操作: echarts  提供一个DOM 元素
  },
  beforeUpdate() {
    // 当页面渲染用的数据 更新的时候触发 但是视图还没有更新
  },
  updated() {
    // 视图更新完成之后触发
  },
  beforeDestroy() {
    // 一般用于像定时器 或者 原生事件绑定 这个类的清除工作
  },
  destroyed() {

  },
}
</script>
<style lang="less">
</style>

4.数组的变异方法

<template>
  <div>
    {{ ary }}
    <button @click="fn">按钮</button>
  </div>
</template>
<script>
// @ is an alias to /src
export default {
  name: 'XXX',
  data() {
    return {
      obj: {
        a: 1, b: 3
      },
      ary: [111, 666, 333]
    }
  },
  methods: {
    fn() {
      // // this.obj.ttt = 666
      // this.$set(this.obj, 'ttt', 666)
      // console.log(this.$set)
      // this.ary[0] = 888
      // push pop unshift shift  splice  reverse  sort
      // this.ary.splice(0, 1, 888)
      // this.$set(this.ary, 0, 888)
      console.log(this.ary.pop == Array.prototype.pop)
      console.log(this.ary.concat == Array.prototype.concat)
      // 通过 Object.create 创造了一个对象 这个对象的__proto__是指向数组的原型的
      //  然后把这七个变异方法 添加到了 这个对象中,在把vue中的数组的原型指向 都换成了
      // 这个对象,那这样的话 当前vue的数组调用push方法的时候其实调用的是这个对象的push方法
      // 这个方法执行的时候 会通知视图更新
    }
  }
}
</script>
<style lang="less">
</style>

5.计算属性+侦听器+过滤器+混入

计算属性+侦听器

<template>
  <div>
    <input type="text" v-model="age">
    <input type="text" v-model="name">
    {{ obj }}
    <button @click="fn">清空</button>
    <ul>
      <li v-for="item in list" :key="item">{{ item }}</li>
    </ul>
  </div>
</template>
<script>
// @ is an alias to /src
/* 
  v-for v-if同时使用 v-for的优先级较高
  v-for和 v-if不建议同时使用  有性能上的浪费
*/
export default {
  data() {
    return {
      age: 100,
      name: "珠峰",
      ary: [111, 222, 333, 25, 67, 46, 89],
      obj: { a: 123 }
    }
  },
  methods: {
    fn() {
      console.log(this)
      // this.name = 999
      // this.ary.push(999)
      this.obj.a = 99
      // this.obj = { a: 999 }
    }
  },
  watch: {
    // 侦听器 侦听的都是this上存在的属性
    // name(newValue, oldValue) {
    //   console.log("只要当name改变了,就会触发这个回调函数", newValue, oldValue)
    // },
    // obj(newValue, oldValue) {
    //   console.log("只要当obj改变了,就会触发这个回调函数", newValue, oldValue)
    // }
    // watch obj 相当 创建了一个watcher 被这个watcher放到 obj的事件池中
    obj: {
      deep: true,// 深度监听  新老值是一样的
      immediate: true,//初始化的时候 执行一次handler
      handler(newVal, oldVal) {
        console.log(newVal, oldVal, '--------------  ')
      }
    }
  },
  computed: {
    // 计算属性中的属性 也会被挂载到当前实例上
    // list() {
    //   // this.ary 和 this.age 称为list的依赖
    //   // 当前函数中"同步"使用的属性 都属于list的依赖
    //   // 只要依赖不发生改变  那么 list对应的这个函数就不会执行 ---> 这就是所谓计算属性有缓存
    // 缓存 靠的是 当前这个watcher的dirty属性 属性是true就会重新执行get获取新值,false就不会执行get
    //   console.log(66666)
    //   setTimeout(() => {
    //     console.log(this.name)
    //   }, 10);
    //   return this.ary.filter(item => item < this.age)
    // }
    list: {
      // 计算属性本质就是Watcher实例; list的值 本质是 当前watcher的value属性值;
      get() {
        return this.ary.filter(item => item < this.age)
      },
      set(val) {
        // 当外界给 list赋予值的时候 就会触发set,val对应的就是被赋予的那个值
        console.log(val)
        this.ary = val

      }
    }
  }
}
</script>
<style lang="less">
</style>

过滤器+混入

import Vue from 'vue'
import App from './App4'
/* 
  混入: 全局混入  局部混入
  混入的一个原则就是  数据以组件自身为准。
  钩子函数 混入的先执行 自己的后执行。
*/
// vuex vue-router 独立插件的可以考虑使用全局混入
// 业务组件最好不要使用
// Vue.mixin({
//   data() {
//     return {
//       age: 111,
//       name: 666
//     }
//   },
//   methods: {
//     fn2() { }
//   },
//   created() {
//     console.log("混入的狗子")
//     this.name = 888
//   }
// })


Vue.filter('format', function (val) {
  // val 对应是 |符 前边的那个值
  return `¥${(val / 100).toFixed(2)}元`
})
let vm = new Vue({
  render: h => h(App),
  methods: {
    qqq() { }
  },
  created() {
    console.log('vm自己的狗子')
  }
}).$mount('#app')
console.log(vm) 

 局部过滤器↓

<template>
  <div>
    <input type="text" v-model="money">
    <h1>当前商品的单价是{{ money | format }}</h1>
    <ul>
      <li v-for="item in goodsList">{{ item.price | format }}</li>
    </ul>
  </div>
</template>
<script>
// @ is an alias to /src
export default {
  data() {
    return {
      money: 1000,
      goodsList: [{ price: 30000 }, { price: 30000 }, { price: 30000 }]
    }
  },
  // filters: {  // 局部过滤器
  //   format(val) {
  //     return `¥${(val / 100).toFixed(2)}元`
  //   }
  // }

}
</script>
<style lang="less">
</style>

局部混入

<template>
  <div>
    <input type="text" v-model="name">
    <input type="text" v-model="age">
    <button @click="fn">===</button>
    <button @click="fn2">===</button>
  </div>
</template>
<script>
// @ is an alias to /src
import js from './App3mixin' // 负责js逻辑抽离
import js2 from './common' //公用逻辑的抽离
export default {
  mixins: [js, js2]
}
</script>
<style lang="less">
</style>

6.nextTick

next原理  node_modules -> vue ->src -> core ->util ->next-tick.js

nextTick本质是 异步+发布订阅 

/* @flow */
/* globals MutationObserver */

import { noop } from 'shared/util'
import { handleError } from './error'
import { isIE, isIOS, isNative } from './env'


/* 
  一个异步 + 发布订阅

*/

export let isUsingMicroTask = false;// 记录的是 使用的异步是否是一个微任务

const callbacks = [];// 一个事件池   用来存储回调函数
let pending = false;// 是否处在一个异步的等待态

function flushCallbacks() {
  // 一个发布的过程  就是用来执行事件池中的所有回调函数 并且清空事件池
  pending = false
  const copies = callbacks.slice(0)
  callbacks.length = 0
  for (let i = 0; i < copies.length; i++) {
    copies[i]()
  }
}

let timerFunc = () => {
  setTimeout(flushCallbacks, 0)
};// 用来存储一个异步函数的

// 这一通判断 其实就是为了创造一个异步函数 原则就是能用微任务就是用微任务 不行 在使用宏任务
/* if (typeof Promise !== 'undefined' && isNative(Promise)) {
  const p = Promise.resolve()
  timerFunc = () => {
    p.then(flushCallbacks)

    if (isIOS) setTimeout(noop)
  }
  isUsingMicroTask = true
} else if (!isIE && typeof MutationObserver !== 'undefined' && (
  isNative(MutationObserver) ||
  // PhantomJS and iOS 7.x
  MutationObserver.toString() === '[object MutationObserverConstructor]'
)) {

  let counter = 1
  const observer = new MutationObserver(flushCallbacks)
  const textNode = document.createTextNode(String(counter))
  observer.observe(textNode, {
    characterData: true
  })
  timerFunc = () => {
    counter = (counter + 1) % 2
    textNode.data = String(counter)
  }
  isUsingMicroTask = true
} else if (typeof setImmediate !== 'undefined' && isNative(setImmediate)) {
  // Fallback to setImmediate.
  // Technically it leverages the (macro) task queue,
  // but it is still a better choice than setTimeout.
  timerFunc = () => {
    setImmediate(flushCallbacks)
  }
} else {
  // Fallback to setTimeout.
  timerFunc = () => {
    setTimeout(flushCallbacks, 0)
  }
} */


export function nextTick(cb, ctx) {
  let _resolve
  callbacks.push(() => {
    if (cb) {
      try {
        cb.call(ctx)
      } catch (e) {
        handleError(e, ctx, 'nextTick')
      }
    } else if (_resolve) {
      _resolve(ctx)
    }
  })
  if (!pending) {
    pending = true
    timerFunc() //异步执行flushcallbacks
  }
  // $flow-disable-line
  if (!cb && typeof Promise !== 'undefined') {
    return new Promise(resolve => {
      _resolve = resolve
    })
  }
}

// 创造一个全局自定义指令

// 一般会一些权限相关的指令

import Vue from 'vue'
import App from './App2.vue'
import Button from './components/button'
console.log(Button);

let userLevel = 2;
/* 
  v-xxx的行内属性
*/
// 创造一个全局自定义指令
// 一般会一些权限相关的指令
Vue.directive('color', function (el, obj) {
  // console.log(el, obj)
  el.style.color = obj.value || '#000'
})
// Vue.directive('permission', function (el, { value }) {
//   if (value > userLevel) {
//     // el.style.display = 'none'
//     el.parentNode.removeChild(el)
//   }
// })
// 函数的写法 其实是 bind和update的一个结合体
Vue.directive('permission', {
  // 自定义指令的钩子函数
  bind() { },
  inserted(el, { value }) {
    // 当前节点被插入到了父节点当中, 
    if (value > userLevel) {
      el.parentNode.removeChild(el)
    }
  },
  update() {

  },
  componentUpdated() { },
  unbind() {
    // console.log(666)
  }
})
Vue.component('my-button', Button)



new Vue({
  render(h) {
    return h(App)
  },
  created() {
    console.log(this);
  },
}).$mount('#app')

ref也可以拿到DOM元素

<template>
  <div>
    <input type="text" v-model="name">
    <h1 v-color ref="qqq">{{ name }}</h1>
    <h1 v-permission="3" v-if="flag">{{ name }}</h1>
    <ul>
      <li v-for="item in ary" ref="li">{{ item }}</li>
    </ul>
  </div>
</template>
<script>
// @ is an alias to /src
import Vue from 'vue'
export default {
  name: 'XXX',
  data() {
    return {
      name: "珠峰",
      col: 'red',
      flag: true,
      ary: [11, 222, 333]
    }
  },
  created() {
    setTimeout(() => {
      this.flag = false
    }, 3000);
  },
  mounted() {
    // console.log(this.$refs.li.length)
    // Vue的DOM更新是一个异步操作
    this.ary.push(444)
    this.$nextTick(() => {
      console.log(this.$refs.li.length)
    })
    this.ary.push(555)
    this.$nextTick(function () {
      console.log(this.$refs.li.length)
    })
    console.log(this.$nextTick)
    //  Vue.prototype.$nextTick = function(cb){return nextTick(cb,this)}
    console.log(Vue.nextTick === this.$nextTick)
    console.log(Vue.prototype.$nextTick === this.$nextTick)

  }
}
</script>
<style lang="less">
</style>

7.组件及数据交互

父组件

<template>
  <div>
    <input type="text" v-model="title">
    <my-list :qqq='title' @ccc='f' />
    <my-button />
  </div>
</template>
<script>
// @ is an alias to /src
import MyList from './components/list'
//  组件的data为啥得是个函数 ?? >> 为了保证数据的独立性
/* 
 A组件中 使用了 B组件 ; 那么 我们就把B称为A的子组件; A称为B的父组件

父传子 是通过自定义属性+props
子传父 是通过自定义事件+$emit
*/
export default {
  name: 'XXX',
  data() {
    return {
      title: "珠峰"
    }
  },
  methods: {
    f(n) {
      console.log("子组件传了一个", n)
      this.title = n
    }
  },
  components: {
    MyList
  }
}
</script>
<style lang="less">
</style>

子组件

<template>
  <div>
    <h1>{{ qqq }}</h1>
    <button @click="fn">修改名字</button>
    <ul>
      <li v-for="item in ary">{{ item }}
        <my-button />
      </li>
    </ul>
  </div>
</template>
<script>
// @ is an alias to /src
/* 
  每一个vue文件的tempate只能有一个根元素  vue3没有这种要求
*/
export default {
  // props data methods computed 里边的属性名不能重复
  // props: ['qqq'],// 这种简写的意思就不做任何限制
  props: {
    // 一般 在创造一些 复用型组件的时候 才会写成 对象格式
    qqq: {
      type: [Number, String],  //对传递的类型做限制
      // required: true, // 必穿属性
      default() { //默认值
        return 'hahaha'
      },
      // validator(val) {
      //   // 若这个函数返回执行是false 那么vue就理解成校验失败了
      //   // return false
      //   if (val.length < 4) {
      //     console.error("要求qqq值的长度不能小于4")
      //     return false
      //   }
      // }
    }
  },
  data() {
    return {
      ary: [111, 222, 333, 444]
    }
  },
  methods: {
    fn() {
      // this.qqq = 888
      this.$emit('ccc', 888)
    }
  },
  created() {
    console.log(this)
  }
}
</script>
<style lang="less">
</style>

# 组件传参

1 - 自定义属性 + props

自定义事件 + $emit

2 - ref 获取组件实例  然后通过实例来使用组件中的数据或者方法

- $children/$parent  

$children 指的是 当前组件中的使用的所有的子组件实例  是个数组;Vue3 移除了这个API

$parent 指的是 当前组件的父组件实例  只有一个

以上3种方式 其实都是通过获取组件实例的方式 来使用对应的数据或者属性

3-provide/inject  祖先组件注册  所有的后代组件都能使用

4-$attrs/$listeners

$attrs 里边存储的是没有被props接收的哪些属性

$listeners 里边存储的是 所有的父组件传进来的事件 ;Vue3 移除了这个API

我们一般会使用这两个api去做一些 ui组件的二次封装

v-bind="$attrs" v-on="$listeners"

5-eventBus: $on $emit $off  本质就是一个发布订阅

6-vuex

7-$root


 

 8.v-model+ sync修饰符+组件插槽

   v-model ==>

      :value + @input

      组件上的表现 :value="xxx" @input="xxx = $event"

      表单上的表现 :value="xxx" @input="xxx = $event.target.value

<template>
  <div>
    <!-- <AA ref="qq" tt="1" yy="2" uu="3" @ff="f" @ff2="f"></AA> -->
    <!-- <button @click="f"></button> -->
    <h1>{{ age }}</h1>
    <!-- <my-input v-model='age' />
    <my-input :value="age" @input="age = $event" /> -->
    <!-- 
      v-model ==> 
      :value + @input 
      组件上的表现 :value="xxx" @input="xxx = $event"
      表单上的表现 :value="xxx" @input="xxx = $event.target.value"
      -->
    <my-input v-model='age' :xxx="name" @update:xxx="name = $event" />
    <my-input v-model='age' :xxx.sync="name" />
  </div>
</template>
<script>
// @ is an alias to /src
import AA from './components/A.vue'
import myInput from './components/my-input2'

export default {
  name: 'XXX',
  data() {
    return {
      name: "珠峰",
      age: 100
    }
  },
  methods: {
    f() {
      // console.log(this.$refs.qq.name)
      // this.$refs.qq.fn()
      console.log(this.$children)
    }
  },
  components: {
    AA, myInput
  }
}
</script>
<style lang="less">
</style>

组件插槽

slot ,作用域插槽(v-slot )、具名插槽(#名字)、匿名插槽

9.父子组件钩子顺序+递归(组件)+keep-alive

/*

  父子组件的钩子函数的执行顺序

  父 beforeCreated -> 父 created -> 父 beforeMount -> 要渲染结构了,发现结构里边有儿子

  子 beforeCreated -> 子 created -> 子 beforeMount -> 渲染儿子的结构 儿子结构渲染完成之后

   子 mounted -> 父 mounted

  父组件更新 子组件不一定更新 若子组件使用了父组件的数据 那么父组件更新子组件才会跟着跟新

  子组件更新 父组件也不一定更新

  父 -> beforeUpdate  -> 子beforeUpdate -> 子 updated  -> 父 updated

  父 -> beforeDestory  -> 子 beforeDestory -> 子 destoryed  -> 父 destoryed

*/

 递归+keep-alive

父组件:

keep-alive

<template>
  <div>
    <!-- <child :list='ary' /> -->
    <button @click="flag = !flag">666</button>
    <!-- :include="/a/"  意思就是说 只缓存哪些 name中包含a的组件 ;  exclude用法一样 意思相反-->
    <!-- :include="['aaa']"  意思就是说 只缓存哪些 name是 aaa 的组件 -->
    <keep-alive :include="/a/">
      <AAA v-if="flag" />
      <BBB v-else />
    </keep-alive>

  </div>
</template>
<script>
// @ is an alias to /src
import child from './components3/child'
import AAA from './components3/AAA'
import BBB from './components3/BBB'
export default {
  name: 'XXX',
  data() {
    return {
      flag: true,
      ary: [
        {
          til: '1',
          children: [
            {
              til: '1-1'
            },
            {
              til: '1-2',
              children: [
                {
                  til: '1-2-1',
                  children: [
                    {
                      til: '1-2-1-1'
                    },
                    {
                      til: '1-2-1-2'
                    }
                  ]
                },
                {
                  til: '1-2-2'
                }
              ]
            }
          ]
        },
        {
          til: 2
        }
      ]
    }
  },
  components: {
    child, AAA,
    BBB
  }
}
</script>
<style lang="less">
</style>

子组件:

这里的qqq 使用的是name v-if item.children 是递归组件

<template>
  <ul class="ul_box">
    <li v-for="item in list" :key="item.til">
      {{ item.til }}
      <qqqq v-if="item.children" :list="item.children"></qqqq>
    </li>
  </ul>
</template>
<script>
// @ is an alias to /src
export default {
  name: 'qqqq',
  props: ['list'],
  data() {
    return {

    }
  },
  components: {

  }
}
</script>
<style lang="less">
</style>

10.keep-alive原理

vue -> src -> core -> components ->keep-alive.js

  原理 就是利用一个对象把加载过的需要缓存的组件 存起来 ;下载再去加载的时候 先看缓存中有没有对应属性

      activated 当前组件被展示的时候触发

      deactivated 是当前组件被隐藏的时候触发

keep-alive源码↓

/* @flow */

import { isRegExp, remove } from 'shared/util'
import { getFirstComponentChild } from 'core/vdom/helpers/index'


// 获取组件的名字
function getComponentName(opts) {
  return opts && (opts.Ctor.options.name || opts.tag)
}
// 判断 组件的名字 是否满足 include 或 exclude
function matches(pattern, name) { // 'qqq,www'
  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
}

function pruneCache(keepAliveInstance, filter) {
  const { cache, keys, _vnode } = keepAliveInstance
  for (const key in cache) {
    const entry = cache[key]
    if (entry) {
      const name = entry.name
      if (name && !filter(name)) {
        pruneCacheEntry(cache, key, keys, _vnode)
      }
    }
  }
}

function pruneCacheEntry(
  cache,
  key,
  keys,
  current
) {
  const entry = cache[key]
  if (entry && (!current || entry.tag !== current.tag)) {
    entry.componentInstance.$destroy()
  }
  cache[key] = null
  remove(keys, key)
}

const patternTypes = [String, RegExp, Array]

export default {
  name: 'keep-alive',
  abstract: true,

  // props: ['include', 'exclude', 'max'],
  props: {
    include: patternTypes,
    exclude: patternTypes,
    max: [String, Number]
  },

  methods: {
    cacheVNode() {
      const { cache, keys, vnodeToCache, keyToCache } = this
      if (vnodeToCache) {
        const { tag, componentInstance, componentOptions } = vnodeToCache
        cache[keyToCache] = {
          name: getComponentName(componentOptions),
          tag,
          componentInstance,
        }
        keys.push(keyToCache)
        // prune oldest entry
        if (this.max && keys.length > parseInt(this.max)) {
          pruneCacheEntry(cache, keys[0], keys, this._vnode)
        }
        this.vnodeToCache = null
      }
    }
  },
  // data() {
  //   return {
  //     cache: Object.create(null),
  //     keys: []
  //   }
  // },

  created() {
    this.cache = Object.create(null) // 存储需要缓存的哪些实例的对象
    this.keys = []// 存储 缓存的实例对应的key
  },

  destroyed() {
    for (const key in this.cache) {
      pruneCacheEntry(this.cache, key, this.keys)
    }
  },

  mounted() {
    this.cacheVNode()
    this.$watch('include', val => {
      pruneCache(this, name => matches(val, name))
    })
    this.$watch('exclude', val => {
      pruneCache(this, name => !matches(val, name))
    })
  },
  // watch: {
  //   include() { },
  //   exclude() { }
  // },

  updated() {
    this.cacheVNode()
  },

  render() {
    const slot = this.$slots.default // 获取keep-alive 包含的内容
    const vnode = getFirstComponentChild(slot)// 获取插槽里的第一个组件实例
    const componentOptions = vnode && vnode.componentOptions// 组件的一些属性
    if (componentOptions) {
      // check pattern
      const name = getComponentName(componentOptions) // keep 包含的那个组件的名字
      const { include, exclude } = this
      if (
        // not included
        (include && (!name || !matches(include, name))) ||
        // excluded
        (exclude && name && matches(exclude, name))
      ) {
        // 若要渲染的那个组件 不满足 include 或者满足 exclude  那么直接使用新创造的实例
        return vnode
      }
      // 能走到这里的 证明 要加载的组件是需要缓存的

      const { cache, keys } = this
      const key = vnode.key == null
        // same constructor may get registered as different local components
        // so cid alone is not enough (#3269)
        ? componentOptions.Ctor.cid + (componentOptions.tag ? `::${componentOptions.tag}` : '')
        : vnode.key
      if (cache[key]) {
        // 若缓存对象中有这一项 那么就直接使用缓存的组件实例
        vnode.componentInstance = cache[key].componentInstance
        // make current key freshest
        // LRU 算法
        remove(keys, key)
        keys.push(key)
        // 因为keys里边去删除多余的key的时候 是从第一个开始删除的
      } else {
        // 缓存对象中没有 当前实例  那么就要把当前实例缓存起来
        this.vnodeToCache = vnode
        this.keyToCache = key
      }

      vnode.data.keepAlive = true
    }
    return vnode || (slot && slot[0])
  }
}

11.异步组件

官方文档:

动态组件 & 异步组件 — Vue.js

 异步组件实际写法↓

<template>
  <div>
    <AA v-if='flag' />
    <BB v-else />
    <CC />
    <button @click="flag = !flag">q q</button>
  </div>
</template>
<script>
// @ is an alias to /src
// import AA from './components/AA'
// import BB from './components/BB'
import myLoading from './components/my-loading'
import CC from './components/CC.jsx'
export default {
  name: 'XXX',
  data() {
    return {
      flag: true
    }
  },
  components: {
    CC,// 异步组件
    AA: () => ({
      component: import('./components/AA'),
      loading: myLoading
    }),
    BB: () => import(/*webpackChunkName:"bbbbbbbb"*/'./components/BB')
  }
}
</script>
<style lang="less">
</style>

render函数

官方地址:渲染函数 & JSX — Vue.js

12.vue插件(vue.use)

vue.use

import Vue from 'vue'
import App from './App3.vue'
function toArray(list, start) {
  start = start || 0
  let i = list.length - start
  const ret = new Array(i)
  while (i--) {
    ret[i] = list[i + start]
  }
  return ret
}
Vue.use = function (plugin) {
  // this._installedPlugins 存储的是已经装过的插件
  // debugge  r
  const installedPlugins = (this._installedPlugins || (this._installedPlugins = []))
  if (installedPlugins.indexOf(plugin) > -1) {
    // r若是已经安装的插件  则 不逊要再去执行下边的安装逻辑了  优化
    return this
  }

  // additional parameters
  const args = toArray(arguments, 1) //从实参集合中 从索引为1的地方把后边的参数放到一个数组力
  // [1, 2, 3, 4, 5]
  args.unshift(this)// [Vue,1, 2, 3, 4, 5]
  if (typeof plugin.install === 'function') {
    plugin.install.apply(plugin, args)
  } else if (typeof plugin === 'function') {
    plugin.apply(null, args)
  }
  installedPlugins.push(plugin)
  return this
}
Vue.use({
  install(_Vue) {
    console.log(arguments)//Vue,1, 2, 3, 4, 5
  }
}, 1, 2, 3, 4, 5)
function plug1() {
  console.log(arguments)//Vue,1, 2, 3, 4, 5
}
plug1.install = function (_Vue) {
  console.log('plug1的instatll')
  _Vue.component()
}
Vue.use(plug1, 1, 2, 3, 4, 5)
Vue.use(plug1, 1, 2, 3, 4, 5)

Vue.component('my-loading2', {
  render(h) {
    return h('h1', { class: 'box', attrs: { qqq: '111' } }, ['loading', h('span', '666')])
  },
  // template
})
new Vue({
  render: h => h(App)
}).$mount('#app')

13.vuex

  原理 就是  在use的时候 执行了vuex的install

  install执行的时候 使用的 全局混入mixin

  全局混入了一个 beforeCreate钩子函数

  这个钩子函数 给每一个组件添加了 $store属性;

import Vuex from 'vuex'
import Vue from 'vue'
Vue.use(Vuex)
// 一旦使用了vuex 那么 每一个组件都可以使用$store了 : 怎么实现的????
/* 
  原理 就是  在use的时候 执行了vuex的install
  install执行的时候 使用的 全局混入mixin
  全局混入了一个 beforeCreate钩子函数 
  这个钩子函数 给每一个组件添加了 $store属性;

*/
let store = new Vuex.Store({
  state: {
    // 放那些公用数据的一个地方
    count: 100
  }
})
// console.log(store)
export default store

  strict: true,// 开启严格模式

state数据 ,

mutations 定义方法接收两个参数 state ,payload传递来的参数, 使用commit

actions

getters 相当于vue的计算属性

import Vuex from 'vuex'
import Vue from 'vue'
Vue.use(Vuex)
let store = new Vuex.Store({
  strict: true, // 开启严格模式
  state: {
    // 想要修改这里边的数据  唯一的方法是提交mutation
    count: 100,
    count2: 100,
    count3: 100
  },
  mutations: {
    // 这里存储的都是一些用来修改vuex数据的方法
    // 执行这里边的方法是通过 store.commit(函数名字,传递的参数)
    changeCount(state, payload) {
      // 这些函数最少有一个参数 最多两个
      state.count = payload

    },
    add(state) {
      state.count++
      return 888
    },
    changeCount2(state, payload) {
      // 这些函数最少有一个参数 最多两个
      state.count = payload.qqq
      // setTimeout(() => {
      //   state.count = payload.qqq
      // }, 1000);

    }
  },
  actions: {
    // 编写一些异步函数 然后 再去调用mutations中的函数
    // 执行这里边的方法是通过 store.dispatch(函数名字,传递的参数)
    changeCountAsync(store, payload) {
      // 这些函数最少有一个参数 最多两个
      setTimeout(() => {
        store.commit('add')
      }, 1000);
      return 888888
    }
  },
  getters: {
    // 这里边的属性 相当于vue的计算属性
    type123(state) {
      return state.count % 2 ? '奇数' : '偶数'
    }
  }
})

export default store

 map辅助函数,例如 mapState, mapMutations, mapActions等

辅助函数有数组和对象两种写法:

数组解构的写法 
...mapState(['count', 'count2', 'count3'])

对象 type即方法名,qqq为payload
 this.$store.commit({
     type: 'changeCount2',
     qqq: this.count + 1
 })

modules模块+vuex数据持久化

模块的使用语法

 components: {
    myA, myB
  },
  methods: {
    add() {
      let ary = [{ text: this.todo, id: Math.random() }, ...this.list]
      this.$store.commit('myA/changeList', ary)
      this.todo = ''
    }
  },
  computed: {
    ...mapState('myA', ['list'])
  }

 vuex数据持久化2中方案

1.监听beforunload事件,页面关闭前存到本地(本质仅支持字符串,所以使用JSON.stringify转换);

模块从本地读取保存的数据
let initData = JSON.parse(localStorage.getItem('vuex/myA')) || {
  name: 'AAA',
  list: [{ text: '弛放', id: 1 }]
}
export default {
  namespaced: true,// 模块独立
  state: initData,
  mutations: {
    changeName(state, name) {
      state.name = name
    },
    changeList(state, arr) {
      state.list = arr
    }
  },
  actions: {}
}

2.使用插件的方式来存储数据 

import Vuex from 'vuex'
import Vue from 'vue'
import AAA from './modules/A'
import BBB from './modules/B'
Vue.use(Vuex)

const myPlugin = store => {
  // 当 store 初始化后调用
  store.subscribe((mutation, state) => {
    // 每次 mutation 之后调用
    // mutation 的格式为 { type, payload }
    console.log(mutation, state)
    if (/myA/.test(mutation)) {
      localStorage.setItem('vuex/myA', JSON.stringify(state.myA))
    }

  })
}
let store = new Vuex.Store({
  strict: true, // 开启严格模式
  plugins: [myPlugin],
  modules: {
    myA: AAA,
    myB: BBB
  }
})
// 如何实现vuex的数据持久化
// window.addEventListener('beforeunload', () => {
//   // 页面关闭之前触发这个事件
//   localStorage.setItem('vuex/myA', JSON.stringify(store.state.myA))
// })
export default store

js 浏览器页面切换事件 - 走看看

页面加水印,但不能影响点击事件(css属性)

css 点击穿透 pointer-events: none;一般用于遮罩_MrLi-2018的博客-CSDN博客_css点击穿透

 14.vueRouter

 hash模式利用的是 hashchange事件

  history模式 利用的是  popstate事件  +  pushState方法和replaceState方法

  redirect 路由重定向; 

router-view 是router创造的一个全局组件;用来展示当前路径对应的那个组件

  路由传参的方式: query  params

  编程式导航: 使用$router实例实现页面跳转

push  replace  go  back  go(-1) == back()

 push 会新增历史记录 ;  replace不会新增历史记录

import Vue from 'vue'
import VueRouter from 'vue-router'
console.log(VueRouter.install)
Vue.use(VueRouter)
let obj = {
  '../components/B.vue': function () { return import('../components/B.vue') },
  '../components/A.vue': function () { return import('../components/A.vue') },
}
let routes = [
  // 路由映射表
  {
    path: '/',
    redirect: '/home'
  },
  {
    path: '/home',
    name: 'home',
    meta: {
      // 路由元信息 这里可以用来存储一些当路径需要某些固定参数 像页面标题  权限之类的
      til: "首页",
      role: ['admin', 'user', 'super']
    },
    component: () => import('../views/home.vue'),
    children: [
      {
        path: '/home/a',
        name: 'home_a',
        component: obj['../components/A.vue'],
        meta: {
          // 路由元信息 这里可以用来存储一些当路径需要某些固定参数 像页面标题  权限之类的
          til: "A页",
          role: ['super']
        },
      }
    ]
  },
  {
    path: '/user',
    name: 'user123',
    meta: {
      // 路由元信息 这里可以用来存储一些当路径需要某些固定参数 像页面标题  权限之类的
      til: "用户页",
      role: ['user']
    },
    component: () => import('../views/user.vue')
  },
  {
    path: '/list/:pn',
    name: 'list123',
    component: () => import('../views/list.vue')
  },
  // {
  //   path: '/*', // 页面的路径跟上边的路由 没有一个匹配上的时候 就会走到这里
  //   redirect: '/home'
  // }
]
let router = new VueRouter({
  routes,
  mode: 'history',
  // linkActiveClass: 'qqq',
  // linkExactActiveClass: 'www'
})

let userRole = 'user'

// router.beforeEach((to, from, next) => {
//   // to 和 from的格式 等同  $route
//   console.log(to, from)
//   document.title = to.meta.til || "珠峰"
//   // if (to.path == '/user') {
//   //   next()
//   // } else {
//   //   next('/user') // next 参数 等同于 push的参数 //使用next进行路径跳转的时候 也会触发前置守卫
//   // }
//   if (!to.meta.role || to.meta.role.includes(userRole)) {
//     //证明有权限  
//     next()
//   } else {
//     next('/404')
//   }
// })

setTimeout(() => {
  // 使用addRoute的时候 要注意  404配置的位置
  let ary = [{
    path: '/home/qqq',
    componentUrl: '../components/B.vue'
  },
  {
    path: '/ttt',
    componentUrl: '../components/B.vue'
  }]

  ary.forEach(item => {
    router.addRoute('home', {
      path: item.path,
      component: obj[item.componentUrl]
      // component: () => import(item.componentUrl)
    })
  })
  router.addRoute({
    path: '/*', // 页面的路径跟上边的路由 没有一个匹配上的时候 就会走到这里
    redirect: '/home'
  })

}, 2000);


export default router
/* 
  hash模式利用的是 hashchange事件
  history模式 利用的是  popstate事件  +  pushState方法和replaceState方法
    前端使用history模式是需要后端配合的,当用户刷选 要返回404的时候 后端不要返回404而是返回我们的html即可

  $route 和 $router的区别????
    $route   是我们用来获取参数的一个都西昂
    $router  是router实例 一边用来调用方法 push replace go back addRoute...  
*/

15.TypeScript(常用)

安装:

cnpm i typescript -g (其他包管理工具也可以)

执行:

tsc helloworld.ts

生成配置文件:

tsc --init

ts中 冒号 后边的都是对前边的那个变量进行的一个类型声明;

number[] 是个数组 而且里边的内容必须是数字

元组 可以理解成一个特殊的数组 特殊在长度固定 内容固定

枚举 可以通过属性名拿到属性值,也可通过属性值拿到属性名

 
  ts中 冒号 后边的都是对前边的那个变量进行的一个类型声明;



let flag: boolean = false;
flag = true;

let a: number = 123;

let b: (string | number) = '';
b = 9

number[] 是个数组 而且里边的内容必须是数字↓
let ary: (number | boolean | string)[] = [1, 2, 3, 4, '', true];

let ary2: Array<number | boolean> = [1, 2, 3, 4, true]

元组 可以理解成一个特殊的数组 特殊在长度固定 内容固定

let ary3: [number, number, boolean] = [1, 1, true]
ary3.push(777)
console.log(ary3)

枚举
enum qqq {
  '零', '一', '二', '三'
}
console.log(qqq['零'])
console.log(qqq[0])

关闭严格模式

void 类型只能接收null或者undefined

never 完整性保证

类型断言和双重断言

字面量type声明

// let n: number = 100
// // n = null // 关闭了严格模式
// n = undefined

// let a: void; //void类型只能接收 null 或者 undefined
// a = null;
// a = undefined

function fn(): void { } // 代表这个函数没有任何返回结果


//  never   完整性保证
function f2(): never {
  throw new Error()
}

// function f(x:number|string){
//   if(typeof x == 'number'){
//     return 1
//   }else if(typeof x == 'string'){
//     return ''
//   }else{

//   }

// }

let ele = document.getElementById('#app');
(ele as HTMLElement).style.color = 'red'// 类型断言; ele肯定是一个html元素

let a: number | string;

// ((a! as any) as boolean) // 双重断言

// 字面量类型
type Role = 'super' | "admin" | 'user'
let role: Role = 'admin'

function f3(a: number, b: number): number {
  return a + b
}






f3(1, 2)

type F = (x: string, y: string) => number
let f4 = (a: number, b: number): number => {
  return a + b
}
let f5: F = (a, b) => {
  return 666
}
f5('1', '2')


// 可选参数
let f6 = function (a: string, b?: string): string {
  return a + b
}
f6('q')

// 默认参数
let f7 = function (a: string, b: string = '6'): string {
  return a + b
}
f7('q')

// 剩余参数

let f8 = function (a: string, ...arg: string[]) { }
f8('q', '', '', '', '')



class Person {
  public name: string    // 这个属性是一个谁都可以访问的一个属性  不写默认就是public
  protected age?: number  // 受保护的属性 只有当前类及其子类可以访问
  private sex: number = 0 // 受保护的属性 只有当前类能访问
  readonly height: number = 180
  constructor(name: string, age: number) {
    this.name = name
    this.age = age
    this.sex = 1
    this.height = 888
  }
  f() {
    console.log(this.age)
    console.log(this.sex)
  }
}
class Student extends Person {
  fn() {
    console.log(this.age)
    console.log(this.sex)
  }
}
let p = new Person('qqq', 123)
// p.name = ''
p.sex

export { }

接口(interface)是一个用来限制对象形状的工具

 interface type 都是ts中的关键字

接口是一个用来限制对象形状的工具
interface type 都是ts中的关键字
interface IObj {
  name: string,
  age: number,
  sex: number,
  qq?: string, // 可有可无
  [qqwwwereee: string]: any  // 上述四个属性是受限制的 其他的属性可有可无 之没有任何限制
}

let obj: IObj = {
  name: "zhufeng ",
  age: 13,
  sex: 1,
  yy: 78,
  uu: true
}
obj.age = 99
obj.qq = '345'


interface IObj1 {
  qq: string
}
interface IObj1 {
  ww: string,
  f(a: string, b: string): string
}

let obj2: IObj1 = {
  qq: '',
  ww: '',
  f(q, y) {
    return ''
  }
}
obj2.f('1', '2')


interface Iobj3 extends IObj1 {
  tt: number
}
// let obj3: Iobj3 = {

// }

泛型 可以理解成一个 类型 变量

// 泛型 可以理解成一个 类型 变量
function createAry<QQQ>(length: number, value: QQQ): QQQ[] {
  let ary = []
  for (let i = 0; i < length; i++) {
    if (i % 2) {
      ary[i] = value
    } else {
      // ary[i] = '111'
    }

  }
  return ary
}

createAry<boolean>(5, true)
createAry<string>(5, 'true')
createAry<number>(5, 2)

interface IUser<T> {
  <QQQQ>(x: T, y: T): QQQQ
}


// let f5: IUser = function <Q>(a: Q, b: Q): Q {
//   return a
// } 
let f6: IUser<number> = function <Q>(a: number, b: number): Q {
  return ('' as any)
}

f6<boolean>(1, 2)
export { }

declare 声明全局变量

| 是两个类型 满足其中一个即可    & 是必须满足所有的;相当于一个合并

// declare 声明全局变量
declare let age: number

declare interface IUser {
  name: string,
  age: number,
  sex: 0 | 1
}

declare type IQQ = {
  q: string,
  w: number
} | {
  t: number
}

declare type IQQ2 = {
  q: string,
  w: number
} & {
  t: number
}

//  | 是两个类型 满足其中一个即可    & 是必须满足所有的;相当于一个合并

Vue3

应用配置 | Vue3中文文档 - vuejs

app.config.globalProperties.$http=function(){}

globalProperties这个属性可以实现 Vue.prototype.xxx的功能

Vue.mixin --> app.mixin

Vue.directive  --> app.directive

Vue.component  --> app.component

Vue.use  --> app.use

API(学习):isRef, markRaw, onMounted, reactive, ref, toRaw, toRef, toRefs, unref

常用:reactive, ref, toRefs

ref

ref的作用就是把一个值类型做成响应式数据

通过ref 产生的响应式数据 在模板中直接使用 JS中需要加上.value

reactive

把一个对象做成一个响应式数据

toReftoRefs

把变量变成响应式

toRef只转一个,toRefs转全部变成响应式

isRef

检测是不是一个Ref对象

unRef

如果参数是一个 ref,则返回内部值,否则返回参数本身。

 通过ref获取dom元素:

 vue3 setup通过ref获取DOM元素或者组件使用ref声明一个响应式数据

 然后返出去 然后模板中直接写ref=‘变量’

生命周期钩子:

官方地址: 生命周期钩子 | Vue.js

props:

  props: ['data'],
  emits: ['qqq'],
  setup(props, ctx) { // props对应的是父组件传进来的数据(前提是上边有props接收)  ctx
    console.log(props.data, ctx)
}

<!-- vue2 :value  + @input -->

<!-- vue3 :modelValue  + @update:modelValue -->

<!-- v-model:count='count' 等价于 vue2 的 :count.sync='count' -->

 计算属性:

在Vue3中需要从Vue中导入computed函数

 let str = computed(() => {
      return `姓名是${userInfo.name};年龄是${userInfo.age}`
    })
    let checkAll = computed({
      get() {
        return state.ary.every(item => item.checked)
      },
      set(val) {
        state.ary.forEach(item => item.checked = val)
      }
    })

侦听器:

Computed 与 watch | Vue.js (官方地址)

在vue中导入 watch

一般 当被监听的内容是一个普通值的时候 需要使用箭头函数返回的格式 ;

监听的是一个响应式数据直接写

    // 一般 当被监听的内容是一个普通值的时候 需要使用箭头函数返回的格式
    watch(() => userInfo.obj, (newV, oldV) => {
      console.log(newV, oldV)
    }, { deep: true })
    watch(userInfo.obj, (newV, oldV) => {
      console.log(newV, oldV)
    })

  // let name2 = toRef(userInfo, 'name') 
    //把userInfo中的name 复制一份变成响应式数据 ;name2会保持对userInfo.name的依赖性

    let name2 = ref(userInfo.name) //把userInfo中的name 复制一份变成响应式数据 
    let name3 = userInfo.name // 把userInfo中的name 复制一份

vuex

开始 | Vuex

import { createStore } from 'vuex'

let store = createStore({
  // 等价于 new Vuex.Store({这个对象})
  state: {
    count: 100
  },
  mutations: {
    add(state, n) {
      state.count += n
    }
  },
  actions: {
    add(store, m) {
      setTimeout(() => {
        store.commit('add', m)
      }, 1000);
    }
  }
})

export default store

import { useStore } from 'vuex'

let store = useStore(); // 等价于 this.$store

vueRouter

入门 | Vue Router

import { createRouter, createWebHistory, createWebHashHistory } from 'vue-router'


let router = createRouter({
  history: createWebHashHistory(), //createWebHashHistory() hash模式;createWebHistory() history模式
  routes: [
    //  路由映射表
    {
      path: '/',
      redirect: '/home'
    },
    {
      path: '/home',
      component: () => import('../views/home.vue')
    },
    {
      path: '/list',
      component: () => import('../views/list.vue')
    },
    {
      path: '/goods/:id',
      component: () => import('../views/goods.vue')
    },
    { path: '/:pathMatch(.*)*', redirect: '/home' },
  ]
})
router.beforeEach((to, from) => {
  console.log(to)
  // return true // 相当于以前的不走next函数了  不写return 和 return true相当于以前的next() 
})

export default router

 在组件中传参

<template>
  <div>
    <h1>{{ count }}</h1>
    <Child />
    <router-link to="/home?q=123&w=567">home</router-link>
    <router-link to="/list">list</router-link>
    <router-link to="/goods/123">123</router-link>
    <router-link to="/goods/456">456</router-link>
    <router-link to="/goods/789">789</router-link>
    <router-view></router-view>
  </div>
</template>

<script>
import { useStore } from 'vuex'
import { computed, watch } from 'vue'
import Child from './child'
import { useRoute } from 'vue-router'
export default {
  components: { Child },
  setup() {
    let store = useStore(); // 等价于 this.$store
    let route = useRoute()
    console.log(store)
    let count = computed(() => store.state.count)
    watch(route, () => {
      // watch:{$route(){}}
      console.log('route')
    })
    return {
      count: count
    }
  }
}
</script>

import { useRoute, useRouter } from 'vue-router'

export default {

  setup() {

    let route = useRoute() // 等价于vue2的this.$route

    let router = useRouter(); // 等价于vue2的this.$router

}}

 Vite项目

开始 {#getting-started} | Vite中文网下一代前端开发与构建工具https://vitejs.cn/guide/

npm7+ 使用模板创建

npm init vite@latest 项目名 -- --template vue-ts(vue-ts模板)

配置@提示

1.在vite.config.ts中输入下方代码

import { resolve } from "path"

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [vue()],
  resolve:{
    alias:{
      '@':resolve(__dirname,'src')
    }
  }
})

2.并在tsconfig.js中添加下方代码

"baseUrl": ".",
    "paths": {
      "@": [
        "src"
      ],
      "@/*": [
        "src*"
      ]
    }

vite-plugin-style-import  该插件可以实现样式导入

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值