乐趣区

关于javascript:Vue30-Composition-API和Hooks使用学习文档

vite 脚手架创立我的项目

1、全局装置 vite 脚手架

npm install -g create-vite-app

2、应用脚手架创立我的项目

create-vite-app projectName

3、进入我的项目文件夹

cd projectName

4、装置依赖

npm install

5、启动 vue3.0 我的项目

npm run dev

降级 Vue-cli

npm update -g @vue/cli
# OR
yarn global upgrade --latest @vue/cli

如果曾经全局装置了旧版本的 vue-cli(1.x 或者 2.x), 你须要先通过

npm uninstall vue-cli -g
// 或
yarn global remove vue-cli

// 卸载它,而后再应用
npm install -g @vue/cli
// 或
yarn global add @vue/cli

// 装置新的包

创立我的项目

npm install -g @vue/cli
vue create 我的项目名
cd 我的项目名
vue add vue-next
npm run serv

开始

在 vue-cli3.0 下装置 composition-api

npm install @vue/composition-api --save
# OR
yarn add @vue/composition-api

在应用任何 @vue/composition-api 提供的能力前,必须先通过 Vue.use() 进行装置

import Vue from 'vue'
import VueCompositionApi from '@vue/composition-api'
Vue.use(VueCompositionApi)

mian.js

import {createApp} from 'vue';
import App from './App.vue';
import router from './router';
import store from './store';
import './App.scss'

const app = createApp(App)
app.use(router).use(store).mount('#app');

Vue-router

import router from './router';
import store from './store';

createApp(App).use(router).use(store).mount('#app');
import {createRouter, createWebHashHistory} from 'vue-router';
import Home from '../views/Home/index.vue';
const routes = [
  {
    path: '/',
    name: 'Home',
    component: Home,
  },
  {
    path: '/home',
    name: 'Home',
    component: Home,
  },
  {
    path: '/login',
    name: 'Login',
    component: () => import('../views/Login/index.vue'),
  },
  {
    path: '/user',
    name: 'User',
    component: () => import('../views/User/index.vue'),
  },
  {
    path: '/information',
    name: 'Information',
    component: () => import('../views/Information/index.vue'),
  },
  {
    path: '/discover',
    name: 'Discover',
    component: () => import('../views/Discover/index.vue'),
  }
];

const router = createRouter({history: createWebHashHistory(),
  routes,
});
router.beforeEach((to, from, next) => {if(to.path === '/') {next({ path: '/home'})
   } else {next()
   }
})
export default router;

Vuex

import Vuex from 'vuex';

export default Vuex.createStore({
  state: {userInfo: {}
  },
  mutations: {setUserInfo(state, info) {state.userInfo = info}
  }, 
  actions: {},});

页面 template

vue2.0 里 template 模板里只能有一个根标签, 而 3.0 能够应用多个根标签

createApp

返回一个提供利用上下文的利用实例。利用实例挂载的整个组件树共享同一个上下文。能够在 createApp 之后链式调用其它办法。该函数第一个参数接管一个根组件选项的对象,第二个参数能够将 props 传递给应用程序.

createApp(
  App,
  {username: 'alex cheng' // 提供 props,App 组件外部通过 props: ['username'] 接管
  }
).mount('#app')

Composition API

数据申明

申明繁多根底数据类型时应用; 定义的变量,函数都要 return 进去;
setup 函数,是在 beforecreate 钩子之前实现的。所以无奈应用 data 跟 methods。另外要留神的是 setup 是同步的,不是异步的。

// 引入形式 1
 import {setup,ref} from 'vue'
 // 引入形式 2
 import {reactive} from '@vue/composition-api'
 export default {setup(props, context) {let count = ref(0)
        console.log(count.value) //0,外部获取时须要用.value 来获取
        console.log(isRef(count)) //true   //isRef() 用来判断是不是 ref 对象
     return {count  // ref 返回的是一个响应式的对象}
    }
 }

Reactive 申明繁多对象时应用

import {setup, Reactive} from 'vue'
export default {
// 倒计时逻辑的 Composition Function
  const useCountdown = (initialCount) => {const count = ref(initialCount)
    const state = ref(false)
    const start = (initCount) => {
      state.value = true;
      if (initCount > 0) {count.value = initCount} 
      if (!count.value) {count.value = initialCount}
      const interval = setInterval(() => {if (count.value === 0) {clearInterval(interval)
          state.value = false
        } else {count.value--}
      }, 1000)
    }
    return {
      count,
      start,
      state
    }
  }
   setup(props, context) {
        const obj = reacttive({
        data1: '123',
        data2: {}})
    const state = reactive({count: 0}) // 创立数据
    // 间接应用倒计时逻辑
     const {count, start, state} = useCountdown(10)
     const onClick = () => {start()
     }
    return {...torefs(obj)// 把 obj 转为响应式的 ref,不然 template 里获取不到,
        state,count, 
        onClick, 
        state
    }
   }
}

watchEffect() 监听 props

import {Reactive} from 'vue'
export default {props: ['val'], // 这里获取 props 和 2.0 一样, 也能够用对象模式
  setup(props, context) {watchEffect(() => {  // 首次和 props 扭转才会执行这外面的代码
         console.log(props.val)
     })
  }
}

ref

ref()函数用来依据给定值创立一个响应式的数据对象,ref()函数的调用返回值是一个对象,这个对象上只蕴含一个 value 属性。
ref 函数只能监听简略类型的变动,不能监听简单类型的变动
导入相干 ref 相干函数

import {ref} from '@vue/composition-api'

创立响应式对象

setup() {const count = ref(0)
        return {
            count,
            name: ref('vue')
        }
}

在 template 中拜访响应式数据

<template>
    <span>{{count}}--- {{name}}</span>
</template>

isRef

isRef 的应用
,isRef() 函数次要用来判断某个值是否为 ref()创立进去的对象;利用场景:当须要开展某个可能为 ref()创立进去的值得时候,例如:

import {isRef} from '@vue/composition-api'
const fooData = isRef(foo) ? foo.value : foo

toRefs

toRefs 的应用,toRefs()函数能够将 reactive()创立进去的响应式对象,转为一般对象,只不过这个对象上的属性节点都是以 ref()类型的像是数据,最常见利用场景

import {toRefs, reactive} from '@vue/composition-api'

setup() {
    // 定义响应式数据对象
    const state = reactive({count: 0})
    // 定义页面上可用的事件处理函数
    const increment = () => {state.count++}
    
    // 在 setup 中返回一个对象供页面应用
    // 这个对象中能够蕴含响应式的数据,也能够蕴含事件处理函数
    return {
      // 将 state 上的每个属性,都转化为 ref 模式的响应式数据
      ...toRefs(state),
      // 自增的事件处理函数
      increment
    }
}

在 template 中就间接能够应用 count 属性和绝对应的 increment 办法了,如果没有应用 roRefs 间接返回 state 那么就得通过 state.xx 来拜访数据

<template>
  <div>
    <span> 以后的 count 值为:{{count}}</span>
    <button @click="increment">add</button>
  </div>
</template>

shallowReactive

听这个 API 的名称就晓得,这是一个浅层的 reactive,难道意思就是本来的 reactive 是深层的呗,没错,这是一个用于性能优化的 API
其实将 obj 作为参数传递给 reactive 生成响应式数据对象时,若 obj 的层级不止一层,那么会将每一层都用 Proxy 包装一次,咱们来验证一下

import {reactive} from 'vue'
export default {setup() {
        const obj = {
            a: 1,
            first: {
                b: 2,
                second: {c: 3}
            }
        }
        const state = reactive(obj)
        console.log(state)
        console.log(state.first)
        console.log(state.first.second)
    }
}

来看一下打印后果:

构想一下如果一个对象层级比拟深,那么每一层都用 Proxy 包装后,对于性能是十分不敌对的
接下来咱们再来看看 shallowReactive

import {shallowReactive} from 'vue'
export default {setup() {
        const obj = {
            a: 1,
            first: {
                b: 2,
                second: {c: 3}
            }
        }
        const state = shallowReactive(obj)
        console.log(state)
        console.log(state.first)
        console.log(state.first.second)
    }
}

来看一下打印后果:

后果十分的明了了,只有第一层被 Proxy 解决了,也就是说只有批改第一层的值时,才会响应式更新,代码如下:

<template>
    <p>{{state.a}}</p>
    <p>{{state.first.b}}</p>
    <p>{{state.first.second.c}}</p>
    <button @click="change1"> 扭转 1 </button>
    <button @click="change2"> 扭转 2 </button>
</template>
<script>
import {shallowReactive} from 'vue'
export default {setup() {
        const obj = {
            a: 1,
            first: {
                b: 2,
                second: {c: 3}
            }
        }
        const state = shallowReactive(obj)
        function change1() {state.a = 7}
        function change2() {
            state.first.b = 8
            state.first.second.c = 9
            console.log(state);
        }
        return {state}
    }
}

来看一下具体过程:

首先咱们点击了第二个按钮,扭转了第二层的 b 和第三层的 c,尽管值产生了扭转,然而视图却没有进行更新;
当咱们点击了第一个按钮,扭转了第一层的 a 时,整个视图进行了更新;
由此可阐明,shallowReactive 监听了第一层属性的值,一旦产生扭转,则更新视图。

shallowRef

这是一个浅层的 ref,与 shallowReactive 一样是拿来做性能优化的
shallowReactive 是监听对象第一层的数据变动用于驱动视图更新,那么 shallowRef 则是监听 .value 的值的变动来更新视图的
咱们来看一下具体代码

<template>
    <p>{{state.a}}</p>
    <p>{{state.first.b}}</p>
    <p>{{state.first.second.c}}</p>
    <button @click="change1"> 扭转 1 </button>
    <button @click="change2"> 扭转 2 </button>
</template>

<script>
import {shallowRef} from 'vue'
export default {setup() {
        const obj = {
            a: 1,
            first: {
                b: 2,
                second: {c: 3}
            }
        }
        const state = shallowRef(obj)
        console.log(state);
        function change1() {
            // 间接将 state.value 从新赋值
            state.value = {
                a: 7,
                first: {
                    b: 8,
                    second: {c: 9}
                }
            }
        }
        function change2() {
            state.value.first.b = 8
            state.value.first.second.c = 9
            console.log(state);
        }
        return {state, change1, change2}
    }
}
</script>

首先看一下被 shallowRef 包装过后是怎么的构造

咱们先点击了第二个按钮,发现数据的确被扭转了,然而视图并没随之更新;
于是点击了第一个按钮,行将整个 .value 从新赋值了,视图就立马更新了
这么一看,未免也太过麻烦了,改个数据还要从新赋值,不要放心,此时咱们能够用到另一个 API,叫做 triggerRef,调用它就能够立马更新视图,其接管一个参数 state,即须要更新的 ref 对象
咱们来应用一下

triggerRef

<template>
    <p>{{state.a}}</p>
    <p>{{state.first.b}}</p>
    <p>{{state.first.second.c}}</p>
    <button @click="change"> 扭转 </button>
</template>

<script>
import {shallowRef, triggerRef} from 'vue'
export default {setup() {
        const obj = {
            a: 1,
            first: {
                b: 2,
                second: {c: 3}
            }
        }
        const state = shallowRef(obj)
        console.log(state);
        function change() {
            state.value.first.b = 8
            state.value.first.second.c = 9
            // 批改值后立刻驱动视图更新
            triggerRef(state)
            console.log(state);
        }
        return {state, change}
    }
}
</script>

咱们来看一下具体过程

能够看到,咱们没有给 .value 从新赋值,只是在批改值后,调用了 triggerRef 就实现了视图的更新

toRaw

toRaw 办法是用于获取 ref 或 reactive 对象的原始数据的
先来看一段代码

<template>
    <p>{{state.name}}</p>
    <p>{{state.age}}</p>
    <button @click="change"> 扭转 </button>
</template>
<script>
import {reactive} from 'vue'
export default {setup() {
        const obj = {
            name: '前端印象',
            age: 22
        }
        const state = reactive(obj)    
        function change() {
            state.age = 90
            console.log(obj); // 打印原始数据 obj
            console.log(state);  // 打印 reactive 对象
        }
        return {state, change}
    }
}
</script>

来看看具体过程

咱们扭转了 reactive 对象中的数据,于是看到原始数据 obj 和被 reactive 包装过的对象的值都产生了变动,由此咱们能够看出,这两者是一个援用关系
那么此时咱们就想了,那如果间接扭转原始数据 obj 的值,会怎么样呢?答案是:reactive 的值也会跟着扭转,然而视图不会更新
由此可见,当咱们想批改数据,但不想让视图更新时,能够抉择间接批改原始数据上的值,因而须要先获取到原始数据,咱们能够应用 Vue3 提供的 toRaw 办法
toRaw 接管一个参数,即 ref 对象或 reactive 对象

<script>
import {reactive, toRaw} from 'vue'
export default {setup() {
        const obj = {
            name: '前端印象',
            age: 22
        }
        const state = reactive(obj)    
        const raw = toRaw(state)

        console.log(obj === raw)   // true
    }
}
</script>

上述代码就证实了 toRaw 办法从 reactive 对象中获取到的是原始数据,因而咱们就能够很不便的通过批改原始数据的值而不更新视图来做一些性能优化了

留神:补充一句,当 toRaw 办法接管的参数是 ref 对象时,须要加上 .value 能力获取到原始数据对象

markRaw

markRaw 办法能够将原始数据标记为非响应式的,即应用 ref 或 reactive 将其包装,仍无奈实现数据响应式,其接管一个参数,即原始数据,并返回被标记后的数据
咱们来看一下代码

<template>
    <p>{{state.name}}</p>
    <p>{{state.age}}</p>
    <button @click="change"> 扭转 </button>
</template>

<script>
import {reactive, markRaw} from 'vue'
export default {setup() {
        const obj = {
            name: '前端印象',
            age: 22
        }
        // 通过 markRaw 标记原始数据 obj, 使其数据更新不再被追踪
        const raw = markRaw(obj)   
        // 试图用 reactive 包装 raw, 使其变成响应式数据
        const state = reactive(raw)    

        function change() {
            state.age = 90
            console.log(state);
        }

        return {state, change}
    }
}
</script>

咱们来看一下在被 markRaw 办法解决过后的数据是否还能被 reactive 包装成响应式数据

从图中能够看到,即便咱们批改了值也不会更新视图了,即没有实现数据响应式

provide && inject

与 Vue2 中的 provide 和 inject 作用雷同,只不过在 Vue3 中须要手动从 vue 中导入
这里简略阐明一下这两个办法的作用:

provide:向子组件以及子孙组件传递数据。接管两个参数,第一个参数是 key,即数据的名称;第二个参数为 value,即数据的值
inject:接管父组件或先人组件传递过去的数据。接管一个参数 key,即父组件或先人组件传递的数据名称

假如这有三个组件,别离是 A.vue、B.vue、C.vue,其中 B.vue 是 A.vue 的子组件,C.vue 是 B.vue 的子组件

// A.vue
<script>
import {provide} from 'vue'
export default {setup() {
        const obj= {
            name: '前端印象',
            age: 22
        }
        // 向子组件以及子孙组件传递名为 info 的数据
        provide('info', obj)
    }
}
</script>
// B.vue
<script>
import {inject} from 'vue'
export default {setup() {    
        // 接管 A.vue 传递过去的数据
        inject('info')  // {name: '前端印象', age: 22}
    }
}
</script>
// C.vue
<script>
import {inject} from 'vue'
export default {setup() {    
        // 接管 A.vue 传递过去的数据
        inject('info')  // {name: '前端印象', age: 22}
    }
}
</script>

获取标签元素

最初再补充一个 ref 另外的作用,那就是能够获取到标签元素或组件
在 Vue2 中,咱们获取元素都是通过给元素一个 ref 属性,而后通过 this.$refs.xx 来拜访的,但这在 Vue3 中曾经不再实用了
接下来看看 Vue3 中是如何获取元素的吧

<template>
  <div>
    <div ref="el">div 元素 </div>
  </div>
</template>
<script>
import {ref, onMounted} from 'vue'
export default {setup() {
      // 创立一个 DOM 援用,名称必须与元素的 ref 属性名雷同
      const el = ref(null)

      // 在挂载后能力通过 el 获取到指标元素
      onMounted(() => {el.value.innerHTML = '内容被批改'})

      // 把创立的援用 return 进来
      return {el}
  }
}
</script>

获取元素的操作一共分为以下几个步骤:

先给指标元素的 ref 属性设置一个值,假如为 el
而后在 setup 函数中调用 ref 函数,值为 null,并赋值给变量 el,这里要留神,该变量名必须与咱们给元素设置的 ref 属性名雷同
把对元素的援用变量 el 返回(return)进来
补充:设置的元素援用变量只有在组件挂载后能力拜访到,因而在挂载前对元素进行操作都是有效的

接管 props 数据

在 props 中定义以后组件容许外界传递过去的参数名称:

props: {name: String}  

通过 setup 函数的第一个形参,接管 props 数据:

setup(props) {console.log(props.name)
}

context 形参

setup 函数的第二个形参是一个上下文对象,就是 vue2.x 中的 this,在 vue 3.x 中,它们的拜访形式如下

 setup(props, context) {
    context.slots
    context.emit
    context.refs
}

watchEffect

在追踪其依赖时立刻运行一个函数,并在依赖发生变化时从新运行

const count = ref(0)

watchEffect(() => console.log(count.value))
// -> logs 0

setTimeout(() => {
  count.value++
  // -> logs 1
}, 100)

勾销察看
接口会返回一个函数,该函数用以勾销察看。

const stop = watchEffect(() => {/* ... */})
// later
stop() // 勾销之后对应的 watchEffect 不会再执行

革除副作用(side effect)

为什么须要革除副作用?有这样一种场景,在 watch 中执行异步操作时,在异步操作还没有执行实现,此时第二次 watch 被触发,这个时候须要革除掉上一次异步操作。

watch(onInvalidate => {const token = performAsyncOperation(id.value)
  onInvalidate(() => {
    // id has changed or watcher is stopped.
    // invalidate previously pending async operation
    token.cancel()})
})

watch 提供了一个 onInvalidate 的副作用革除函数,该函数接管一个函数,在该函数中进行副作用革除。在以下状况中调用:

watch 的 callback 行将被第二次执行时。
watch 被进行时,即组件被卸载之后

接口还反对配置一些选项以扭转默认行为,配置选项可通过 watch 的最初一个参数传入。

flush

当同一个 tick 中产生许多状态渐变时,Vue 会缓冲观察者回调并异步刷新它们,以防止不必要的反复调用。
默认的行为是:当调用观察者回调时,组件状态和 DOM 状态曾经同步。
这种行为能够通过 flush 来进行配置,flush 有三个值,别离是 post(默认)、pre 与 sync。sync 示意在状态更新时同步调用,pre 则示意在组件更新之前调用。

watchEffect(() => {/* ... */},
  {flush: 'sync'}
)

onTrack 与 onTrigger

用于调试:
onTrack:在 reactive 属性或 ref 被追踪为依赖时调用。
onTrigger:在 watcher 的回调因依赖扭转而触发时调用。

watchEffect(() => {/* side effect */},
  {onTrigger(e) {debugger}
  }
)

onTrack 与 onTrigger 仅实用于开发模式。

mixin

 import {ref} from 'vue'
 export const useMyMinxin = {const name = ref('qimukakax')
  const getToken = () => {
      const params = 'qimukakax'
      getToken()}
}

将对象混入以后的模版

<template>
    <div>components</div>
</template>

<script>
import useMyMinxin  from './minxin.js'
import {onMounted} from 'vue'
export defaut {setup() {const { name, getToken} = useMyMinxin()
      onMounted(() => console.log(name)) 
  }
}
</script>

readonly

readonly 函数接管一个对象(一般对象或者 reactive 对象)或者 ref,并返回一个只读的原始参数对象代理,在参数对象扭转时,返回的代理对象也会相应扭转。如果传入的是 reactive 或 ref 响应对象,那么返回的对象也是响应的。
简而言之就是给传入的对象新建一个只读的正本。

const original = reactive({count: 0})
const copy = readonly(original)
watchEffect(() => {
  // 原始对象的变动会登程正本的 watch
  console.log(copy.count)
})
// 原始对象扭转,正本的值也会扭转
original.count++
// 正本不可更改
copy.count++ // warning!

watch 监听器

监听单个数据

import {Reactive} from 'vue'
export default {setup(props, context) {let count1 = ref(0)
   let state = reactive({count2: 0)
    // 监听 reactive 类型
   watch(() => state.count2,// 这里是你要监听的数据
   (count,preCount) => { //count 新值,preCount 旧值
       console.log('') // 这里是监听数据变动后执行的函数
   }, {lazy:false}// 在第一次创立不监听)
   // 监听 ref 类型
     watch(count1,(count,preCount) => { //count 新值,preCount 旧值
       console.log('') // 这里是监听数据变动后执行的函数
     })
   return {
       count
       ...toRefs(state)
   }
  }
}

监听多个数据

import {setup, Reactive} from 'vue'
export default {setup(props, context) {let count1 = ref(0)
    let name1 = ref(0)
   let state = reactive({
      count2: 0,name2:'yangy'
   )
    // 监听多个 reactive 类型
   watch([() => state.count2, () => state.name2]
       ([count,name], [preCount,preName]) => { //count 新值,preCount 旧值
           console.log('') // 这里是监听数据变动后执行的函数
       }, 
       {lazy:false})// 在第一次创立不监听
   
   // 监听 ref 类型
  watch([count2, name2]
       ([count,name], [preCount,preName]) => { //count 新值,preCount 旧值
           console.log('') // 这里是监听数据变动后执行的函数
       }, {lazy:false}// 在第一次创立不监听)
   
   return {
       count,...toRefs(state)
   }
  }
}

具体应用

应用前导入 import {watch} from '@vue/composition-api'
const count = ref(0)
// 定义 watch 只有 count 的值变动,就会触发 watch 的回调
// watch 会在创立的时候主动调用一次

watch(() => console.log(count.value))
setTimeout(() => {count.value++}, 1000)

监听指定数据

// 定义 reactive 数据源
const state = reactive({count: 0})
// 监督 state.count 这个数据节点的变动
watch(() => state.count, (now, prev) => {console.log(now, prev)
})
/ 定义 ref 数据源
const count = ref(0)
// 监督 count 这个数据的变动
watch(count, (now, prev) => {console.log(now, prev)
})

computed

计算属性 可创立只读,和可读可写两种
computed() 用来创立计算属性,computed() 函数的返回值是一个 ref 的实例。应用 computed 之前须要按需导入:

import {computed} from '@vue/composition-api'

创立只读的计算属性,在调用 computed()函数的时候,传入一个 function 函数,能够失去一个只读的计算属性。

// 创立一个响应式数据
const count = ref(1) 
// 依据 count 的值创立一个响应式的计算属性,它会依据 ref 主动计算并且返回一个新的 ref
const computedCount = computed(() => count.value + 1) 
console.log(computedCount.value) // 打印 2
computedCount.value++ // 报错

创立可读可写的计算属性
在调用 computed()函数的时候传入一个蕴含 get 和 set 的对象,就能够失去一个可读可写的计算属性了。

// 创立一个响应式数据
const count = ref(1) 
// 依据 count 的值创立一个响应式的计算属性,它会依据 ref 主动计算并且返回一个新的 ref
const computedCount = computed({get: () => count.value + 1,
    set: (val) => count.value = val - 1
} ) 

computedCount.count = 6
console.log(count.value) // 打印 5
import {setup, Reactive} from 'vue'
export default {setup(props, context) {let count = ref(0)
  setup () {const count = ref(0)
      const addCount = computed(() => count.value + 1) // 只读,count.value 变动后执行加 1
      
      const addCount2 = computed({get:() => count.value + 1,
          set: (value) => count.value = value 
      })    
      // addCount2.value = 10   // 赋值办法
     return {
         count,
         addCount,
         addCount2
     }
  }
  }
}
import {setup} from 'vue'
export default {setup(props, context) {
       // 这里的 props 大家应该晓得是啥吧,父组件传的值
       // context 是什么?// 在 setup()里咱们不能用 this
       // 所以 vue2.0 里的 this.$emit, this.$psrent, this.$refs 在这里都不能用了。//context 就是对这些参数的汇合
       //context.attrs
       //context.slots
       //context.parent 相当于 2.0 里 this.$psrent
       //context.root 相当于 2.0 里 this
       //context.emit 相当于 2.0 里 this.$emit
       //context.refs 相当于 2.0 里 this.$refs
       ...
   }
}

provide / inject

provide 和 inject 启用依赖注入。只有在应用以后流动实例的 setup() 期间能力调用这两者。

// 父组件
setup() {
  const globalData = reactive({name: 'alex.cheng'})
  
  provide('globalData', globalData)
}

// 子 / 孙 组件
setup() {const globalData = inject('globalData')
}

留神: 能够在 setup 中获取,也能够在生命周期 (onMounted/onBeforeMount) 中获取。然而不能在办法中获取,比方点击事件在办法中打印,会是 undefined, 并且 Vue 会给出正告。

function showInject () {
  // 获取顶层组件的数据
  const globalObj = inject('globalObj')
  console.log(globalObj)
}

defineComponent

顾名思义,这是一个定义组件的办法,传递一个蕴含组件选项的对象

import {defineComponent, h} from 'vue'
const DefineComp = defineComponent({data () {
    return {count: 11}
  },
  render () {
    return h(
      'h1',
      {class: 'define-comp'},
      `${this.count}`
    )
  }
})

或者是一个 setup 函数(函数名将作为组件名来应用)

import {defineComponent, ref} from 'vue'
const HelloWorld = defineComponent(function HelloWorld() {const count = ref(0)
  return {count}
})

getCurrentInstance

getCurrentInstance 反对拜访外部组件实例,用于高阶用法或库的开发。只能在 setup 或生命周期钩子中调用。
留神: 如需在 setup 或生命周期钩子外应用,请先在 setup 中调用 getCurrentInstance() 获取该实例而后再应用。

const MyComponent = {setup() {const internalInstance = getCurrentInstance() // works
    const id = useComponentId() // works
    const handleClick = () => {getCurrentInstance() // doesn't work
      useComponentId() // doesn't work

      internalInstance // works
    }
    onMounted(() => {getCurrentInstance() // works
    })

    return () =>
      h(
        'button',
        {onClick: handleClick},
        `uid: ${id}`
      )
  }
}

// 在组合式函数中调用也能够失常执行
function useComponentId() {return getCurrentInstance().uid
}

LifeCycle Hooks(生命周期)

在新版中的生命周期须要按需导入,并且只能写 setup()函数中。
应用 onBeforeMount, onMounted, updated 相干生命周期,应用前导入相干 api 办法

<template>
  <div id="app"></div>
</template>

<script>
// 1. 从 vue 中引入 多个生命周期函数
import {onBeforeMount, onMounted, onBeforeUpdate, onUpdated, onBeforeUnmount, unMounted} from 'vue'
export default {
  name: 'App',
  setup() {onBeforeMount(() => {// 在挂载前执行某些代码})
      onMounted(() => {// 在挂载后执行某些代码})
      onBeforeUpdate(() => {// 在更新前前执行某些代码})
      onUpdated(() => {// 在更新后执行某些代码})
      onBeforeUnmount(() => {// 在组件销毁前执行某些代码})
      unMounted(() => {// 在组件销毁后执行某些代码})
      return {}}
  
}
</script>

相干每个生命周期办法都是传入一个 function 函数。
vue2.x 与新版 Composition API 之间的映射关系

beforeCreate -> setup
created -> setup
beforeMount -> onBeforeMount
mounted -> onMounted
beforeUpdate -> onBeforeUpdate
updated -> onUpdated
beforeDestroy -> onBeforeUnmount
destroyed -> onUnmounted
errorCaptured -> onErrorCaptured

provide & inject(数据共享)

provide()和 inject()能够实现嵌套组件之间的数据传递。这个两个函数只能在 setup()函数中应用。父级组件中应用 provide()函数能够使数据向下传递,子组件中应用 inject()接管下层传过来的数据。
实现代码:
根组件(父组件)parent.vue

<template>
  <div>
    <child-one></child-one>
    <child-two></child-two>
  </div>
</template>

<script>
  import {provide} from '@vue/composition-api'
  import ChildOne from '@/components/Child'
  import ChildTwo from '@/components/Child'
  export default {
    components: {
       ChildOne,
       ChildTwo
    },
    setup() {// provide('要共享的数据名称', 被共享的数据)
      provide('globalName', 'vue') 
    }
  }
</script>

子组件 1 ChildOne.vue

<template>
  <div>
    <!-- 页面展现数据 globalName -->
    {{globalName}} 
  </div>
</template>

<script>
  import {inject} from '@vue/composition-api'
  export default {
    name: 'ChildOne',
    setup() {const globalName = inject('globalName') 调用 inject 函数时,通过指定的数据名称,获取到父级共享的数据
      return {globalName}
    }
  }
</script>

子组件 2 ChildTwo.vue

<template>
  <div>
    <!-- 页面展现数据 globalName -->
    {{globalName}} 
  </div>
</template>

<script>
  import {inject} from '@vue/composition-api'
  export default {
    name: 'ChildTwo',
    setup() {const globalName = inject('globalName') 调用 inject 函数时,通过指定的数据名称,获取到父级共享的数据
      return {globalName}
    }
  }
</script>

provide 函数被共享的数据能够使 ref 和 reactive 定义的响应式数据,用法相似
template refs(元素组件)
咱们晓得在 vue2.x 中获取页面元素的 DOM 能够通过 ref 写在页面元素上而后在 js 中通过 $refs.x 来拿取以后元素的 DOM 元素信息,操作 DOM, 在 composition-api 中咱们通过提供的 ref 办法传入一个 null 并且定义与页面元素上 ref 绝对应。
代码实现:

<template>
  <div>
    <h1 ref="h1Ref">Hello Word</h1>
  </div>
</template>
<script>
import {ref, onMounted} from '@vue/composition-api'
export default {setup() {
    // 创立一个 DOM 援用
    const h1Ref = ref(null)
    // 在 DOM 首次加载结束之后,能力获取到元素的援用
    onMounted(() => {
      // 为 dom 元素设置字体色彩
      // h1Ref.value 是原生 DOM 对象
      console.log(h1Ref.value)
    })
    // 把创立的援用 return 进来
    return {h1Ref}
  }
}
</script>

Suspense 组件

在开始介绍 Vue 的 Suspense 组件之前,咱们有必要先理解一下 React 的 Suspense 组件,因为他们的性能相似。
React.lazy 承受一个函数,这个函数须要动静调用 import()。它必须返回一个 Promise,该 Promise 须要 resolve 一个 default export 的 React 组件。

import React, {Suspense} from 'react';
const myComponent = React.lazy(() => import('./Component'));
function MyComponent() {
  return (
    <div>
      <Suspense fallback={<div>Loading...</div>}>
        <myComponent />
      </Suspense>
    </div>
  );
}

Vue3 也新增了 React.lazy 相似性能的 defineAsyncComponent 函数,解决动静引入(的组件)。defineAsyncComponent 能够承受返回承诺的工厂函数。当您从服务器检索到组件定义时,应该调用 Promise 的解析回调。您还能够调用 reject(reason)来批示负载曾经失败

import {defineAsyncComponent} from 'vue'
const AsyncComp = defineAsyncComponent(() =>
  import('./components/AsyncComponent.vue')
)
app.component('async-component', AsyncComp)
Vue3 也新增了 Suspense 组件:
<template>
  <Suspense>
    <template #default>
      <my-component />
    </template>
    <template #fallback>
      Loading ...
    </template>
  </Suspense>
</template>

<script lang='ts'>
 import {defineComponent, defineAsyncComponent} from "vue";
 const MyComponent = defineAsyncComponent(() => import('./Component'));

export default defineComponent({
   components: {MyComponent},
   setup() {return {}
   }
})
</script>

Application Config

errorHandler

顶层谬误捕捉

app.config.errorHandler = (err, vm, info) => {console.log(err)
}

warnHandler

顶层正告捕捉

app.config.warnHandler = function(msg, vm, trace) {console.log(msg)
}

globalProperties

全局配置项,相似 Vue2 的 Vue.prototype.$http = $axios; 用法

app.config.globalProperties.foo = 'bar'

isCustomElement

这个 Api 的作用在于可能把第三方或者自定义而没有在 Vue 中注册标签应用时,疏忽正告。

<template>
    <haha-Hello>123</haha-Hello>
</template>
export default {name: 'hello'}

失常状况下是会报正告的,但这个 Api 就能配置疏忽这个正告,标识这是我自定义的组件。
用法:
app.config.isCustomElement = tag => tag.startsWith(‘haha-‘)

留神:目前这个 Api 是有问题的,请看 girhub issues
这里提供了一些解决方案,Vue 作者尤雨溪也阐明了,这个 Api 目前有点问题:

As pointed out, Vue 3 requires configuring custom elements via compiler options if pre-compiling templates.
如前所述,如果是预编译模板,则 Vue 3 须要通过编译器选项配置自定义元素。
This seems to be now a Vue CLI specific configuration problem so I’m closing it. But feel free to continue the discussion.
当初这仿佛是 Vue CLI 特定的配置问题,因而我将其敞开。然而请随时持续探讨。

从中提到了,预编译模板 (template) 应用自定义标签,须要通过编译器选项配置自定义元素,从 girhub issues
中能够看到一个答案,在 vite 上的解决方案:

vite.config.js:
vueCompilerOptions: {
    isCustomElement: tag => {return /^x-/.test(tag)
    }
}

具体能够看 Vite 的 Api:github vite Api 中的 config 在配置项:config.ts 就能够找到 Vue 编译选项配置字段:vueCompilerOptions
这样配置后就能够疏忽上诉例子的正告了:

vueCompilerOptions: {
    isCustomElement: tag => {return /^haha-/.test(tag)
    }
}

optionMergeStrategies
这个 Api 是只针对于 options Api 的,作用是对 mixin 的合并更改策略。

const app = Vue.createApp({custom: 'hello!'})
app.config.optionMergeStrategies.custom = (parent, child) => {console.log(child, parent)
  // => "goodbye!", undefined
  // => "hello", "goodbye!"
  return child || parent
}
app.mixin({
  custom: 'goodbye!',
  created() {console.log(this.$options.custom) // => "hello!"
  }
})

这里能够看到,在 created 输入的时候,输入的是 hello,就是因为设置了合并策略,当组件和 mixin 存在雷同属性的时候,会应用 child 的值,当不存在自定义属性反复的时候,以后组件输入的就是 child 因为这时候 parent 为 undefined
www.zhihu.com/question/40… 什么时候执行 render 函数

Directive

Vue2:

<div id="hook-arguments-example" v-demo:[foo].a.b="message"></div>
Vue.directive('demo', {bind: function (el, binding, vnode) {
    var s = JSON.stringify
    el.innerHTML =
      'name:'       + s(binding.name) + '<br>' +
      'value:'      + s(binding.value) + '<br>' +
      'expression:' + s(binding.expression) + '<br>' +
      'argument:'   + s(binding.arg) + '<br>' +
      'modifiers:'  + s(binding.modifiers) + '<br>' +
      'vnode keys:' + Object.keys(vnode).join(',')
  }
})
new Vue({
  el: '#hook-arguments-example',
  data: {
    foo: 'HaHa'
    message: {color: 'white', text: 'hello!'}
  }
})
/*
 * name: "demo"
 * value: {color: 'white', text: 'hello!'}
 * expression: "message"
 * argument: "HaHa"
 * modifiers: {a: true, b: true}
 * name: "tag, data, children, text, elm, ns, context, fnContext, fnOptions, fnScopeId, key, componentOptions, componentInstance, parent, raw, isStatic, isRootInsert, isComment, isCloned, isOnce, asyncFactory, asyncMeta, isAsyncPlaceholder"
 **/

Vue3:
Vue3.x 和 Vue2.x 的指令在生命周期上有这显著差异,但应用是差不多的

import {createApp} from 'vue'
const app = createApp({})
// register
app.directive('my-directive', {
  // called before bound element's attributes or event listeners are applied
  created() {},
  // called before bound element's parent component is mounted
  beforeMount() {},
  // called when bound element's parent component is mounted
  mounted() {},
  // called before the containing component's VNode is updated
  beforeUpdate() {},
  // called after the containing component's VNode and the VNodes of its children // have updated
  updated() {},
  // called before the bound element's parent component is unmounted
  beforeUnmount() {},
  // called when the bound element's parent component is unmounted
  unmounted() {}
})

// register (function directive)
app.directive('my-directive', () => {// this will be called as `mounted` and `updated`})
// getter, return the directive definition if registered
const myDirective = app.directive('my-directive')
instance: 应用指令的组件实例。value: 传递给指令的值。例如,在 v -my-directive =“1 + 1”中,该值为 2。oldValue: 旧的值,仅在 beforeUpdate 和更新时可用。值是否已更改都可用。arg: 参数传递给指令(如果有)。例如,在 v-my-directive:foo 中,arg 为“foo”。modifiers: 蕴含修饰符(如果有)的对象。例如,在 v -my-directive.foo.bar 中,修饰符对象为 {foo:true,bar:true}。dir: 一个对象,在注册指令时作为参数传递。例如,在指令中
app.directive('focus', {mounted(el) {el.focus()
  }
})
dir 就是:{mounted(el) {el.focus()
  }
}

use 和 plug

如何制作插件和应用插件?
请看以下案例:

// 自定义 plug 插件
// myUi.js
import MyButton from './MyButton.vue';
import MyInput from './MyInput.vue';
const componentPool = [
    MyButton,
    MyInput
];
export default {install () {if (options.components) {option.components.map((compName) => {componentPool.map((comp) => {if (compName === comp.name) {app.component(comp.name, comp);
                    }
                })            
            })
        } else {
            componentPool.map(comp => {app.component(comp.name, comp);
            })
        }
    }
}

myUi 该插件,简略的实现了一下按需加载 UI 的计划

// main.js
import {createApp} from 'vue';
import App from './App.vue';
import MyUI from './libs/MyUI';
const app = createApp(App);
app.use(MyUI, {
    components: [
        'MyButton',
        'MyInput'
    ]
})

markRaw

import {reactive, markRaw} from 'vue';
export defalut {
    name: 'App',
    setup() {let obj = {name: 'Benson', age: 18};
        obj = markRow(obj);
        let state = reactive(obj);
        function fn() {state.name = 'zs';}
        return {state, fn}
    }
}

markRaw 的作用就是禁止源数据不能被用于监听,通过上述解决后,reactive 解决 obj 进行响应式数据的封装将不在其作用了。

customRef

创立一个自定义的 ref,并对其依赖项跟踪和更新触发进行显式管制。它须要一个工厂函数,该函数接管 track 和 trigger 函数作为参数,并应返回一个带有 get 和 set 的对象。
应用 v-model 应用自定义 ref 实现 debounce 的示例:
Html

<input v-model="text" />
function useDebouncedRef(value, delay = 200) {
  let timeout
  return customRef((track, trigger) => {
    return {get() {track()
        return value
      },
      set(newValue) {clearTimeout(timeout)
        timeout = setTimeout(() => {
          value = newValue
          trigger()}, delay)
      }
    }
  })
}
export default {setup() {
    return {text: useDebouncedRef('hello')
    }
  }
}

shallowReadonly

const state = shallowReadonly({
  foo: 1,
  nested: {bar: 2}
});
// 扭转状态自身的 property 将失败
state.foo++;
// ... 但实用于嵌套对象
isReadonly(state.nested); // false
state.nested.bar++; // 实用

只读成果,仅仅管制在 第一层,深层的属性还是能够变更新的
还有一个留神点,ES6 的 const 也能管制只读,但请看上面例子:

const argu_1 = {name: '小明', attrs: {sex: 18}};
const argu_2 = readonly({name: '小红'}, attrs: {sex: 16});
const argu_3 = shallowReadonly({name: '小夏'}, attrs: {sex: 17});
argu_1 管制这该变量不可变更,然而能够变更外面的属性
argu_2 管制着每一层属性都不可变更,然而 argu_2 能够从新赋值
argu_3 管制着第一层属性不可变更,深层的属性能够变更,比方 sex 是能够变更的,argu_3 能够从新赋值。

emits 定义自定义事件

这个语法相似于 vue 组件中应用 props 校验传入的参数。
例子:

app.component('custom-form', {
  // 数组形式, 只对自定义事件名称校验
  emits: ['click', 'submit'],
  // 对象形式
  emits: {
    // 没有验证
    click: null,
    // 验证 submit 事件
    submit: ({email, password}) => {if (email && password) {return true} else {console.warn('Invalid submit event payload!')
        return false
      }
    }
  },
  methods: {submitForm() {this.$emit('submit', { email, password})
    }
  }
})

script scoped 反对全局规定或只针对插槽内容的规定

deep 选择器

<style scoped>
    /* deep selectors */
    ::v-deep(.foo) {}
    /* shorthand */
    :deep(.foo) {}
</style>

最后,反对 >>> 组合器以使选择器为“deep”。然而,某些 CSS 预处理器(例如 SASS)在解析它时会遇到问题,因为这不是官网的 CSS 组合器。
起初切换到 / deep /,这已经是 CSS 的理论倡议增加(甚至是 Chrome 自身提供的),但起初删除了。这引起了一些用户的困惑,因为他们放心在 Vue SFC 中应用 / deep / 会导致在删除该性能的浏览器中不反对其代码。然而,就像 >>> 一样,/ deep / 仅由 Vue 的 SFC 编译器用作编译时提醒以重写选择器,并在最终 CSS 中被删除。
为了防止失落的 / deep / 组合器造成凌乱,引入了另一个自定义组合器:: v-deep,这次更加明确地表明这是 Vue 特定的扩大,并应用伪元素语法,以便任何 - 处理器应该可能解析它。
因为以后 Vue 2 SFC 编译器的兼容性起因,仍反对深度组合器的先前版本,这又可能使用户感到困惑。在 v3 中,咱们不再反对 >>> 和 / deep /。
在钻研实用于 v3 的新 SFC 编译器时,咱们留神到 CSS 伪元素实际上在语义上不是组合符。伪元素改为承受参数,这与习用 CSS 更加统一,因而,咱们也使:: v-deep()那样工作。如果您不关怀显式的 v - 前缀,也能够应用更短的:deep()变体,其工作原理完全相同。
以后仍反对:: v-deep 作为组合器,但已将其弃用,并会收回正告。
插槽款式

<style scoped>
    /* targeting slot content */
    ::v-slotted(.foo) {}
    /* shorthand */
    :slotted(.foo) {}
</style>

以后,从父级传入的 slot 内容受父级的作用域款式和子级的作用域款式的影响。无奈编写仅明确指定 slot 内容或不影响 slot 内容的规定。
在 v3 中,咱们打算默认使子范畴的款式不影响 slot 的内容。为了显式地指定插槽内容,能够应用:: v-slotted()(简写为:: slotted())伪元素。
scoped 状况下定义全局款式

<style scoped>
    /* one-off global rule */
    ::v-global(.foo) {}
    /* shorthand */
    :global(.foo) {}
</style>

以后,要增加全局 CSS 规定,咱们须要应用独自的无作用域块。咱们将引入一个新的:: v-global()(简写为:: global())伪元素,以用于一次性全局规定。
试验状态的个性

script setup

<template>
  <button @click="inc">{{count}}</button>
</template>
<script setup="props, {emit}">
  import {ref, onMounted} from 'vue'
  export const count = ref(0)
  export const inc = () => count.value++
  onMounted(() => {emit('foo');
  )
</script>

script vars

反对将组件状态驱动的 CSS 变量注入到“单个文件组件”款式中。
案例:

<template>
  <div class="text">hello</div>
</template>
<script>
export default {data() {
    return {color: 'red'}
  }
}
</script>
<style vars="{color}">
.text {color: var(--color);
}
</style>

留神的一点,目前 Vue SFC 款式提供了间接的 CS 配置和封装,然而它是纯动态的 - 这意味着到目前为止,尚无奈依据组件的状态在运行时动静更新款式。
当款式存在 scoped 部分款式和又要应用全局 var 的状况:

<style scoped vars="{color}">
h1 {color: var(--color);
  font-size: var(--global:fontSize);
}
</style>

这里的 fontSize 是全局的 var css 变量
通过 compiles 后:

h1 {color: var(--6b53742-color);
  font-size: var(--fontSize);
}

引入 element 组件库

element-plus 组件库:https://element-plus.gitee.io…
element-plus 仓库地址:https://github.com/element-pl…
引入

import ElementPlus from 'element-plus'
import 'element-plus/lib/theme-chalk/index.css'
import {createApp} from 'vue'
import App from './App.vue'

createApp(App).use(ElementPlus).mount('#app')

vue 打包优化

1. 配置 gzip 压缩,打进去一个待 gzip 后缀的文件

$ npm i compression-webpack-plugin -D
const CompressionWebpackPlugin = require('compression-webpack-plugin');
module.exports = {
    configureWebpack: config => {if (process.env.NODE_ENV === 'production') {
            config.plugins.push(
                ...[
                    new CompressionWebpackPlugin({filename: '[path].gz[query]',
                        algorithm: 'gzip',
                        test: /\.(js|css|html|svg)$/i,
                        threshold: 2048,
                        minRatio: 0.8
                    })
                ]
            );
        }
    }
};

2. webpack-bundle-analyzer 剖析包

$ npm i webpack-bundle-analyzer -D
module.exports = {
    chainWebpack: config => {if (process.env.NODE_ENV === 'production') {
            // 启动时动态创建一个 html:http://localhost:8888/report.html
            // config.plugin('webpack-bundle-analyzer').use(require('webpack-bundle-analyzer').BundleAnalyzerPlugin);
            // 生成一个动态 html,report.html
            config.plugin('webpack-report').use(require('webpack-bundle-analyzer').BundleAnalyzerPlugin, [
                {analyzerMode: 'static'}
            ]);
        }
    }
};

3. webpack splitChunks,提取进去的通用 ‘echarts’, ‘moment’, ‘element-ui’, ‘xlsx’ 等

// ['echarts', 'moment', 'element-ui', 'xlsx', 'chunk-vendors', 'chunk-common', 'index']
module.exports = {
    pages: {
        index: {
            // ...
            // 在这个页面中蕴含的块,默认状况下会蕴含
            // 提取进去的通用 chunk 和 vendor chunk。chunks: ['echarts', 'moment', 'element-ui', 'xlsx', 'chunk-vendors', 'chunk-common', 'index']
        },
        chainWebpack: config => {if (process.env.NODE_ENV === 'production') {
                // vue-cli3.x+ 默认配置
                // config.optimization.splitChunks({
                //     chunks: 'async',
                //     minSize: 30000,
                //     maxSize: 0,
                //     minChunks: 1,
                //     maxAsyncRequests: 6,
                //     maxInitialRequests: 4,
                //     automaticNameDelimiter: '~',
                //     cacheGroups: {
                //         vendors: {
                //             name: 'chunk-vendors',
                //             test: /[\\/]node_modules[\\/]/,
                //             priority: -10,
                //             chunks: 'initial'
                //         },
                //         common: {
                //             name: 'chunk-common',
                //             minChunks: 2,
                //             priority: -20,
                //             chunks: 'initial',
                //             reuseExistingChunk: true
                //         }
                //     }
                // });
                config.optimization.splitChunks({
                    chunks: 'async',
                    minSize: 1024 * 10, // 30000,
                    maxSize: 0,
                    minChunks: 1,
                    maxAsyncRequests: 6,
                    maxInitialRequests: 4,
                    automaticNameDelimiter: '~',
                    cacheGroups: {
                        // 链接:https://juejin.cn/post/6844904105555525640
                        echarts: {
                            name: 'echarts',
                            test: /[\\/]node_modules[\\/]echarts[\\/]/,
                            minSize: 0,
                            minChunks: 1,
                            reuseExistingChunk: true,
                            chunks: 'all'
                        },
                        moment: {
                            name: 'moment',
                            test: /[\\/]node_modules[\\/]moment[\\/]/,
                            minSize: 0,
                            minChunks: 1,
                            reuseExistingChunk: true,
                            chunks: 'all'
                        },
                        'element-ui': {
                            name: 'element-ui',
                            test: /[\\/]node_modules[\\/]element-ui[\\/]/,
                            minSize: 0,
                            minChunks: 1,
                            reuseExistingChunk: true,
                            chunks: 'all'
                        },
                        xlsx: {
                            name: 'xlsx',
                            test: /[\\/]node_modules[\\/]xlsx[\\/]/,
                            minSize: 0,
                            minChunks: 1,
                            reuseExistingChunk: true,
                            chunks: 'all'
                        },

                        vendors: {
                            name: 'chunk-vendors',
                            test: /[\\/]node_modules[\\/]/,
                            priority: -10,
                            chunks: 'initial'
                        },
                        common: {
                            name: 'chunk-common',
                            minChunks: 2,
                            priority: -20,
                            chunks: 'initial',
                            reuseExistingChunk: true
                        }
                    }
                });
            }
        }
    }
}

4. momentjs 优化

计划 1:只打包应用的文件(目前用的时这个)

vue.config.js
module.exports = {
    configureWebpack: config => {
        config.plugins.push(
            ...[
                // 链接:https://juejin.cn/post/6844904105555525640
                new webpack.ContextReplacementPlugin(/moment[/\\]locale$/, // 这个参数表明了咱们要扭转的打包上下文
                    /zh-cn/ // 这个参数示意咱们只想打包这个正则匹配的文件
                )
            ]
        );
    }
};

计划 2:应用 dayjs 代替 moment,代码不必变,把 lodash 配置别名为 dayjs

知乎上的文章:zhuanlan.zhihu.com/p/61031739?…

module.exports = {
    chainWebpack: config => {
        config.resolve.alias
        // set 第一个参数:设置的别名,第二个参数:实在的名称(默认都是从 node_modules 中读取
        .set('moment','dayjs'));
        }
};
  • 计划 3:…

5. lodash 优化

$ npm i lodash-webpack-plugin babel-plugin-lodash -D
babel.config.js
module.exports = {presets: ['@vue/app'],
    plugins: [
        'lodash',
        [
            'component',
            {
                libraryName: 'element-ui',
                styleLibraryName: 'theme-chalk'
            }
        ]
    ]
};
vue.config.js
const LodashModuleReplacementPlugin = require('lodash-webpack-plugin');
module.exports = {
    configureWebpack: config => {
        config.plugins.push(
            ...[new LodashModuleReplacementPlugin(),
                // 链接:https://juejin.cn/post/6844904105555525640
                new webpack.ContextReplacementPlugin(/moment[/\\]locale$/, // 这个参数表明了咱们要扭转的打包上下文
                    /zh-cn/ // 这个参数示意咱们只想打包这个正则匹配的文件
                )
            ]
        );
    }
};

6. 小图片压缩成 base64 格局

module.exports = {
    chainWebpack: config => {
        // 10kb 以内的图片会被打包成内联元素
        config.module
            .rule('images')
            .use('url-loader')
            .loader('url-loader')
            .tap(options => Object.assign(options, {limit: 10240}));
    }
};

7. xlsx 改为 xlsx.mini.min

把所有

import XLSX from 'xlsx';

改为以下的形式引入

import XLSX from 'xlsx/dist/xlsx.mini.min.js';

8. http1.1 降级 http2.0

留神的点:

  1. nginx version 1.9.4 以上
  2. 以后配置 http2 基于 https 协定
nginx 配置文件 nginx.conf
    # http 转 https
    server {
        listen 80;
        server_name yourdomain.com; #须要将 yourdomain.com 替换成证书绑定的域名。rewrite ^(.*) https://$server_name$1 permanent; #将所有 HTTP 申请通过 rewrite 指令重定向到 HTTPS。}
    #以下属性中,以 ssl 结尾的属性示意与证书配置无关。server {
        # 开启 gzip
        gzip on;

        # 启用 gzip 压缩的最小文件,小于设置值的文件将不会压缩
        gzip_min_length 1k;

        # gzip 压缩级别,1-9,数字越大压缩的越好,也越占用 CPU 工夫,前面会有具体阐明
        gzip_comp_level 9;

        # 进行压缩的文件类型。javascript 有多种形式。其中的值能够在 mime.types 文件中找到。gzip_types text/plain application/javascript application/x-javascript text/css application/xml text/javascript application/x-httpd-php image/jpeg image/gif image/png application/vnd.ms-fontobject font/ttf font/opentype font/x-woff image/svg+xml;

        # 是否在 http header 中增加 Vary: Accept-Encoding,倡议开启
        gzip_vary on;

        # 禁用 IE 6 gzip
        gzip_disable "MSIE [1-6]\.";

        # 设置压缩所须要的缓冲区大小
        gzip_buffers 32 4k;

        # 设置 gzip 压缩针对的 HTTP 协定版本
        gzip_http_version 1.1;
        
        # 留神这里 http2
        listen 443 ssl http2;
        #配置 HTTPS 的默认拜访端口为 443。#如果未在此处配置 HTTPS 的默认拜访端口,可能会造成 Nginx 无奈启动。#如果您应用 Nginx 1.15.0 及以上版本,请应用 listen 443 ssl 代替 listen 443 和 ssl on。server_name yourdomain.com; #须要将 yourdomain.com 替换成证书绑定的域名。root html;
        index index.html index.htm;
        ssl_certificate cert/cert-file-name.pem;  #须要将 cert-file-name.pem 替换成已上传的证书文件的名称。ssl_certificate_key cert/cert-file-name.com.key; #须要将 cert-file-name.key 替换成已上传的证书密钥文件的名称。ssl_session_timeout 5m;
        ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4;
        #示意应用的加密套件的类型。ssl_protocols TLSv1 TLSv1.1 TLSv1.2; #示意应用的 TLS 协定的类型。ssl_prefer_server_ciphers on;
        # location / {
        #     root html;  #站点目录。#     index index.html index.htm;
        # }
        location / {
            # 反向代理
            proxy_pass http://127.0.0.1:8090;
        }
        error_page 404 /404.html;
            location = /40x.html { }
        error_page 500 502 503 504 /50x.html;
            location = /50x.html {}}

http2.0 network 成果

9. SEO 优化

<meta name="description" content="vue,element-ui,nodejs,express,nginx 全栈治理后盾我的项目" />

vue-router(4.x)

import {createRouter} from "vue-router"
const router = createRouter({
    // options
    .....
})

路由模式
createWebHashHistory (hash)
createWebHashHistory (history)

import {createRouter,createWebHashHistory,createWebHashHistory} from 'vue-router'
const router = createRouter({history:createWebHashHistory() / createWebHashHistory()})

重定向

{path: '/:pathMatch(.*)*', // 须要应用正则去匹配
    redirect: Home,
}

挂载形式

因为 vue3 的 composition api,vue-router 的挂载形式以插件来挂载
4.x 的用法

import {createApp} from 'vue'
import router from './router.js'
import App from './App.vue'
createApp(App).use(router).mount('#app');

组件中的应用
因为 setup 中不能访 this, 所以提供两个 api 来获取 router 和 route,useRouter() 和 useRoute()
4.x 的用法

   import {useRouter,useRoute} from "vue-router"
   export default({setup(){const router = useRouter();
        const route = useRoute();
        const linkToHome = () => {
            router.push({path:'/'})
        }
        return{linkToHome}
      }
   })

vue3.0 的生态

官网:https://v3.vuejs.org/
源码:https://github.com/vuejs/vue-…
vite 构建器:https://github.com/vitejs/vite
脚手架:https://cli.vuejs.org/
vue-router-next:https://github.com/vuejs/vue-…
vuex4.0:https://github.com/vuejs/vuex…

UI 组件库

vant2.x:https://vant-contrib.gitee.io…
Ant Design of Vue 2.x:https://2x.antdv.com/docs/vue…
element-plus:https://element-plus.org/#/zh-CN

参考文章

Vue3.0 尝鲜:https://juejin.cn/post/684790…
Vue3.0 && Vue3.0 初体验 一:https://juejin.cn/post/684790…
Vue3.0 尝试 https://juejin.cn/post/684490…
记 Vue3.0 :https://juejin.cn/post/690677…
初识 Vue3.0 :https://juejin.cn/post/690314…
Vue3.0 尝鲜:https://juejin.cn/post/684490…
Vue3.0 变动简介 :https://juejin.cn/post/685954…
Vue3.0 学习笔记 :https://juejin.cn/post/690818…
Vue3.0 系列——「vue3.0 学习手册」第一期 :https://juejin.cn/post/688684…
Vue3.0 实现 todoList 案例 :https://juejin.cn/post/691501…
Vue3.0 体验 (API 及 TodoList Demo):https://juejin.cn/post/692335…
疾速应用 Vue3 最新的 15 个罕用 API:https://juejin.cn/post/689703…
2021 新年 Vue3.0 + Element UI 尝鲜小记:https://juejin.cn/post/691491…
你 30 分钟疾速把握 vue 3 :https://juejin.cn/post/688735…
Vue 3.0 新个性与应用 三 :https://juejin.cn/post/691678…
Vue 3.0 新个性与应用 二 :https://juejin.cn/post/690231…
vue-cli3.0 打包优化实际:https://juejin.cn/post/691353…
Vue3.0 && Vue3.0 初体验 一:https://juejin.cn/post/684790…
vue3.0 正式版体验:https://juejin.cn/post/688338…
学习笔记:vue-router(4.x) 与 vue-router(3.x)的区别:https://juejin.cn/post/691268…

退出移动版