Vue3 的新特性经过 Vue 的做者尤雨溪的视频而来:Vue.js 做者谈 Vue 3.0 beta 现状html
Webpack 是有 Tree-shaking 这个功能的,但 Tree Shaking 前提是 ES Module(import…) 来写。Vue 3 在浏览器中,依然会有一个全局的 Vue对象,可是在用 Webpack 的时候,它就没有defualt export,你就不能 import vue,把 Vue 当对象自己去操做,全部的 Api 都要 import 进来。这样使得一些不会被用到的功能不会被引用进来。vue
文件大小变化很明显,22.5KB ~ 13.5KB,若是使用 Composition 新语法,只有 11.75KBreact
Composition API RFCwebpack
Vue3 在 setup 把一个对象返回,会把对象变成响应式,而后,Vue 在须要的地方去追踪它所用到的响应式的依赖,当依赖变化的时候从新去渲染。(reactive)git
vue 3 里暴露一个新的 Api 叫作 watchEffect,Effect 就是反作用和 React 的 useEffect 相似。github
一些原生的数据结构,好比像数字、Boolean 等,这些就须要用一个东西包装(ref)web
核心:reactive \ ref \ watchEffect \ 其余是这三者的组合使用。算法
组件再也不须要一个惟一的根节点了vue-cli
Vue 2 模板只有一个单独的根节点。typescript
Vue 3 文字、多个节点、甚至也能够是个v-for,都会自动变成碎片,若是使用渲染函数,也能够直接在渲染函数里面返回一个数组,自动变成一个碎片。
对标 React Portal
<teleport>
向 Vue 核心添加组件 该组件须要一个目标元素,该元素是经过须要一个 HTMLElement
或 querySelector
字符串的 prop
提供的。
组件将其子代移动到 DOM
选择器标识的元素 在虚拟 DOM
级别,子级仍然是子代的后代 <teleport>
,所以他们能够从其祖先那里得到注入
异步组件
和 React 的 Suspense 相似
<Suspense>
<template #default>
异步的组件
</template>
<template #fallback>
Loading ...(加载状态的组件)
</template>
</Suspense>
复制代码
Vue3.0 中支持 自定义渲染器 (Renderer):这个 Api 能够用来建立自定义的渲染器, (在以往像 weex 和 mpvue,须要经过 fork 源码的方式进行扩展)
Vue3 提供了一种新工具 vite,它在程序开发阶段,采用了 Koa、Node 开启一个服务和游览器对 import 的支持实现了按需请求加载内容,避免了热更新时长时间的编译过程,这大大提升了开发效率。
固然发布生产的时候,仍是经过 Webpack 进行编译打包流程。
官方 Cli 工具,记得要升级最新版本
npm install -g @vue/cli
vue create 01-vue3-cli
cd 01-vue3-cli
<!--安装 Vue3 库-->
vue add vue-next
npm run serve
复制代码
vue-cli 一开始尚未支持的时候,vue 官网整了一个 webpack 的项目配置,直接 clone 便可
git clone https://github.com/vuejs/vue-next-webpack-preview.git 01-vue3-webpack
cd 01-vue3-webpack
npm install
npm run dev
复制代码
这是一个 Vue 全新的开发工具,是由 Vue 的做者开发的,目的是之后取代 webpack,原理就是利用游览器如今已经支持 ES6 的 import
语法,遇见 import
会发送一个 http 请求去加载文件,vite 拦截这些请求,作一些预编译,就省去了 webpack 漫长的打包时间,提高开发效率。
npm install -g create-vite-app
create-vite-app 01-vue3-vite
cd 01-vue3-vite
npm install
npm run dev
复制代码
运行项目后,打开 http://localhost:3000/ 看一下 network 就会发现,全部的文件都是 import
进行了预编译,而后经过游览器发起请求,作到了天生的按需加载,秒开项目。
<template>
<div>
<h1>{{state.count}} * 2={{double}}</h1>
<button @click="add">累加</button>
</div>
</template>
<script>
export default {
data: () => ({
state: {
count: 1
}
}),
computed: {
double() {
return this.state.count * 2;
}
}
methods: {
add() {
this.state.count++;
}
}
}
</script>
复制代码
从上面的简单例子能够看到,在 Vue2 的处理方式就是这样的。
下面咱们看一下改用 Vue3 的 Composition api 模式
<template>
<h1>{{state.count}} * 2={{double}}</h1>
<button @click="add">累加</button>
</template>
<script>
import {reactive,computed} from 'vue'
export default {
setup(){
const state = reactive({
count:1
})
function add(){
state.count++
}
const double = computed(()=>state.count*2)
return {state,add,double}
}
}
</script>
复制代码
Composition api 的方式写,从上面例子中,虽然看不出来有什么好处,那是由于组件规模还不是很庞大,其实在组件变得愈来愈庞大以后,优点就会愈来愈明显了,后续会展现出来。
setup
是新的选项,能够理解是 Composition 的入口,函数内部在 beforeCreate
以前调用,函数返回的内容会做为模板渲染的上下文
其实和 Vue2 里的 Vue.observerable
是同样的,把一个数据变成响应式,这个能力是彻底独立的。
关于 Vue.observerable
的用法看这里:cn.vuejs.org/v2/api/#Vue…
其实就是一个计算属性,和 Vue2 的能力是同样的
Vue2 里面的 data
,methods
,computed
都是挂载在 this
之上的,有两个明显的缺点
computed
功能,代码也会被打包Vue3 的按需手动 import
写法更有利于 Tree-shaking 打包
在 Vue3 中 reactive
负责复杂的数据结构,ref
能够吧基本的数据结构包装成响应式的
<template>
<h1>{{state.count}} * 2={{double}}</h1>
<h2>{{num}}</h2>
<button @click="add">累加</button>
</template>
<script>
import {reactive,computed,ref,onMounted} from 'vue'
export default {
setup(){
const state = reactive({
count:1
})
const num = ref(2)
function add(){
state.count++
num.value+=10
}
const double = computed(()=>state.count*2)
onMounted(()=>{
console.log('mouted')
})
return {state,add,double,num}
}
}
</script>
复制代码
能够看到,当要包装独立基本数据类型的时候,就可使用上 ref
了
<script>
import {reactive,computed,ref,onMounted} from 'vue'
export default {
setup(){
const {state,double} = useCounter(1)
const num = ref(2)
function add(){
state.count++
num.value+=10
}
onMounted(()=>{
console.log('mouted')
})
return {state,add,double,num}
}
}
function useCounter(count,n){
const state = reactive({
count
})
const double = computed(()=>state.count*2)
return {state,double}
}
</script>
复制代码
这里能够看到,这样写的话,就很像 React 的 Hook 写法了,这样的作法在后续项目愈来愈庞大的时候就能体现出很好的优点出来了。
看了上面的各类写法后,当咱们在开发一个组件的时候,须要把数据放到 data
,把计算属性放到 computed
,把方法放到 methods
,这种作法虽说将不一样的功能对应放到响应的位置,但也带来了一个问题,当组件十分庞大的时候,会发现对组件的维护变得十分难受,当修改一个功能时须要在组件上下处处去翻腾,先后修改代码,这也是 大圣老师 所说的上下反复横跳的问题。
能够看看官方的例子:
// 更改成 Vue3 写法后
export default {
setup () {
// Network
const { networkState } = useNetworkState()
// Folder
const { folders, currentFolderData } = useCurrentFolderData(networkState)
const folderNavigation = useFolderNavigation({ networkState, currentFolderData })
const { favoriteFolders, toggleFavorite } = useFavoriteFolders(currentFolderData)
const { showHiddenFolders } = useHiddenFolders()
const createFolder = useCreateFolder(folderNavigation.openFolder)
// Current working directory
resetCwdOnLeave()
const { updateOnCwdChanged } = useCwdUtils()
// Utils
const { slicePath } = usePathUtils()
return {
networkState,
folders,
currentFolderData,
folderNavigation,
favoriteFolders,
toggleFavorite,
showHiddenFolders,
createFolder,
updateOnCwdChanged,
slicePath
}
}
}
复制代码
优点就因而可知了。
这个功能呢就是 Vue 组件能够不用必定要一个根节点了,能够这样写:
<template>
<h1>H1</h1>
<div>Div</div>
</template>
复制代码
这种写法在 Vue2 中是会报错的
下面再来看一个快速排序算法组件小例子:
<template>
<quick :data="left" v-if="left.length"></quick>
<li>{{flag}}</li>
<quick :data="right" v-if="right.length"></quick>
</template>
<script>
export default {
name:'quick',
props:['data'],
setup(props){
let flag = props.data[0]
let left = []
let right = []
props.data.slice(1).forEach(v=>{
v>flag? right.push(v): left.push(v)
})
return {left, right, flag}
}
}
</script>
复制代码
<ul>
<FragmentDemo :data="[5,3,1,6,9,4,2,8]" />
</ul>
复制代码
运行后的结果呈现出来是这样的:
<ul>
<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>
</ul>
复制代码
这个组件和 React 的 Portal 组件是相似的,用起来比较简单,就是能够渲染 vue 组件的内容,到指定的 dom 节点中。
在作弹窗的时候,比较有用,由于每每,咱们的弹窗都须要渲染到最外层的 body 下面,不然嵌套过多,蒙层可能会被父元素的 transform 影响。
经常使用场景:公共的模态框 Modal
下面来看一下小例子:
<template>
<h1>{{ msg }}</h1>
<div class="confirm-modal">
<button @click="isOpen = true">打开</button>
<!-- 注意这一块代码 -->
<Teleport to="#modal-container">
<div class="modal-warp" v-if="isOpen">
<div class="cover"></div>
<div class="content">
<p>我在外部哦</p>
<button @click="isOpen = false">取消</button>
</div>
</div>
</Teleport>
</div>
</template>
<script>
import { ref } from 'vue';
export default {
name: 'HelloWorld',
props: {
msg: {
type: String,
default: 'Teleport 使用案例',
},
},
setup() {
const isOpen = ref(false);
return { isOpen };
},
};
</script>
<style>
.modal-warp {
position: fixed;
width: 100%;
height: 100%;
top: 0;
left: 0;
display: flex;
justify-content: center;
align-items: center;
}
.cover {
position: absolute;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.2);
}
.content {
position: absolute;
padding: 16px 32px;
background-color: white;
border-radius: 4px;
}
</style>
复制代码
上面的 <Teleport to="#modal-container">
表示,接下来将会把里面的内容渲染到 id 为 modal-container
的 dom 节点中。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite App</title>
</head>
<body>
<div id="app"></div>
<div id="modal-container"></div>
<script type="module" src="/src/main.js"></script>
</body>
</html>
复制代码
运行的效果就是这样滴:
Suspense 的功能是一个异步组件加载的功能,这个在 React 中也存在一个相似
React
import React, { Suspense } from 'react';
const OtherComponent = React.lazy(() => import('./OtherComponent'));
function MyComponent() {
return (
<div>
<Suspense fallback={<div>Loading...</div>}>
<OtherComponent />
</Suspense>
</div>
);
复制代码
Vue3
<Suspense>
<template #default>
异步的组件
</template>
<template #fallback>
加载状态的组件
</template>
</Suspense>
复制代码
这里也将展现一个小例子:
父组件
<template>
<h1>Supense</h1>
<Suspense>
<template #default>
<AsyncComponent :timeout="3000" />
</template>
<template #fallback>
<h1>加载中</h1>
</template>
</Suspense>
</template>
<script>
import AsyncComponent from './AsyncComponent.vue';
export default {
components: {
AsyncComponent,
},
};
</script>
复制代码
AsyncComponent 组件
<template>
<h1>一个异步小组件</h1>
</template>
<script>
function sleep(timeout) {
return new Promise((resolve) => setTimeout(resolve, timeout));
}
export default {
name: 'AsyncComponent',
props: {
timeout: {
type: Number,
required: true,
},
},
async setup(props) {
await sleep(props.timeout);
},
};
</script>
复制代码
这样基本就能体现出来了,运行结果以下:
答:
- beforeCreate
- created
答:
- 使用了 proxy 代替了 Object.defineProperty
- 新增 Composition Api
- 让 vue 更快的优化
- 更好的 ts 支持,vue3 直接用 ts 重写
答:
- 更容易维护
- 更多的原生支持
- 更易于开发使用
答:
- Vue3 会兼容以前的写法,Composition API 也是可选的
- 为了继续支持 IE11,Vue3 将发布一个支持旧观察者机制和新 Proxy 版本的构建
- Vue3 不只会使用 TypeScript,并且许多软件包将被解耦,使全部内容更加模块化
答:
对
答:
页面首次加载会触发 beforeCreate、created、beforeMount、mounted、beforeUpdate、updated
正确的是:
beforeCreate、created、beforeMount、mounted
答:
- props
- provide / inject
- listeners
解析:
context 属于 react 中的一种跨层级传参方式