Vue 3.0 的变化
- 源码组织方式的变化
- 采用 Typescript重写
- 采用Monoorpo管理项目结构
- composition API
- Options API包含一个描述组件的选项(data、methods、props等)的对象
- Options API 开发复杂组件,同一个功能逻辑代码会被拆分到不同选项
- Vue 3.0 新增一组基于函数的 API,可以更灵活的组织组件的逻辑
- 性能提升
- 响应式系统升级
- Vue.js 2.0 x 中响应式系统的核心 defineProperty
- Vue.js 3.0 x 中使用 Proxy 对象重写响应式系统
- 编译优化
- Vue.js 2.0 x 中通过标记静态节点,优化 diff 的过程
- Vue.js 3.0 x 中标记和提升所有的静态根节点,diff 的时候只需要对比动态内容
- Fragments(升级 Vuter 插件)
- 静态提升
- Patch flag
- 缓存事件处理函数
- 源码体积优化
- Vue.js 3.0 x 中移除了一些不常用的 API
- 例如: inline-template filter 等
- Tree-shaking
- Vue.js 3.0 x 中移除了一些不常用的 API
- 响应式系统升级
Vite
现代浏览器都支持 ES Module (IE不支持)
通过下面的方式加载模块
<script type="module" src="..."></script>
支持模块的script默认延时加载
Vite 在生产环境使用 Rollup 打包
- 基于 ES Module 的方式打包
-
Vite 创建项目
Vite创建项目
npm init vite-app <project-name>cd <project-name>npm installnpm run dev
基于模板创建项目
npm init vite-app --template reactnpm init vite-app --template preact
Composition API
生命周期钩子函数

setup()是在beforeCreate和created之间执行的,不需要这两个生命周期函数
reactive/toRefs/ref
- reactive 将一个对象变成响应式对象
- toRefs 将 reactive 返回的响应式对象所有属性添加为响应式
-
computed
需要传入一个函数 ```javascript import { createApp, reactive, computed } from ‘./node_modules/vue/dist/vue.esm-browser.js’ const data = [ { text: ‘看书’, completed: false }, { text: ‘敲代码’, completed: false }, { text: ‘约会’, completed: true } ]
createApp({ setup () { const todos = reactive(data)
const activeCount = computed(() => {return todos.filter(item => !item.completed).length})return {activeCount,push: () => {todos.push({text: '开会',completed: false})}}
} }).mount(‘#app’)
<a name="1XGxi"></a>### Watch- Watch 的三个参数- 第一个参数:要监听的数据(ref/reactive 的返回值)- 第二个参数:监听到数据变化的执行函数,这个函数有两个参数分别是新值和旧值- 第三个参数:选项对象,deep 和 immediate- Watch 的返回值- 取消监听函数<a name="rjFCt"></a>### WatchEffect- 是 Watch 函数的简化版本,也用来监听数据的变化- 接收一个函数作为参数,监听函数内响应式数据的变化<a name="6Upsw"></a>## 响应式原理<a name="EnlV9"></a>### reactive- 接收一个参数,判断这个参数是否为对象- 创建拦截器对象 handler,设置 get、set、deleteProperty- 返回 Proxy 对象```javascriptconst isObject = val => val !== null && typeof val === 'object'const convert = target => isObject(target) ? reactive(target) : targetconst hasOwnProperty = Object.prototype.hasOwnPropertyconst hasOwn = (target, key) => hasOwnProperty.call(target, key)export function reactive (target) {if (!isObject(target)) return targetconst handler = {get (target, key, receiver) {// 收集依赖track(target, key)const result = Reflect.get(target, key, receiver)return convert(result)},set (target, key, value, receiver) {const oldValue = Reflect.get(target, key, receiver)let result = trueif (oldValue !== value) {result = Reflect.set(target, key, value, receiver)// 触发更新trigger(target, key)}return result},deleteProperty (target, key) {const hadKey = hasOwn(target, key)const result = Reflect.deleteProperty(target, key)if (hadKey && result) {// 触发更新trigger(target, key)}return result}}return new Proxy(target, handler)}
收集依赖
effect && track
let activeEffect = nullexport function effect (callback) {activeEffect = callbackcallback() // 访问响应式对象属性,去收集依赖activeEffect = null}let targetMap = new WeakMap()export function track (target, key) {if (!activeEffect) returnlet depsMap = targetMap.get(target)if (!depsMap) {targetMap.set(target, (depsMap = new Map()))}let dep = depsMap.get(key)if (!dep) {depsMap.set(key, (dep = new Set()))}dep.add(activeEffect)}
trigger
export function trigger (target, key) {const depsMap = targetMap.get(target)if (!depsMap) returnconst dep = depsMap.get(key)if (dep) {dep.forEach(effect => {effect()})}}
ref
export function ref (raw) {// 判断 raw 是否是ref 创建的对象,如果是的话直接返回if (isObject(raw) && raw.__v_isRef) {return}let value = convert(raw)const r = {__v_isRef: true,get value () {track(r, 'value')return value},set value (newValue) {if (newValue !== value) {raw = newValuevalue = convert(raw)trigger(r, 'value')}}}return r}
reactive vs ref
- ref 可以将基本数据类型转换为响应式对象
- ref 返回对象,重新赋值成对象也是响应式的
- reactive 返回对象,重新赋值丢失响应式
-
toRefs
```javascript export function toRefs (proxy) { const ret = proxy instanceof Array ? new Array(proxy.length) : {}
for (const key in proxy) { ret[key] = toProxyRef(proxy, key) }
return ret }
function toProxyRef (proxy, key) { const r = { __v_isRef: true, get value () { return proxy[key] }, set value (newValue) { proxy[key] = newValue } } return r }
<a name="Z4Aos"></a>### computed```javascriptexport function computed (getter) {const result = ref()effect(() => (result.value = getter()))return result}
Vite
概念
- Vite是一个面向现代化浏览器的一个更轻、更快的Web应用开发工具
它基于ECMAScript标准原生模块系统(ES Modules)实现
项目依赖
Vite
- @vue/compiler-scf
Vite实现原理
静态Web服务器(Koa) ```javascript!/usr/bin/env node
const path = require(‘path’) const { Readable } = require(‘stream’) const Koa = require(‘koa’) const send = require(‘koa-send’) const compilerSFC = require(‘@vue/compiler-sfc’)
const app = new Koa()
const streamToString = stream => new Promise((resolve, reject) => { const chunks = [] stream.on(‘data’, chunk => chunks.push(chunk)) stream.on(‘end’, () => resolve(Buffer.concat(chunks).toString(‘utf-8’))) stream.on(‘error’, reject) })
const stringToStream = text => { const stream = new Readable() stream.push(text) stream.push(null) return stream }
// 3. 加载第三方模块 app.use(async (ctx, next) => { // ctx.path —> /@modules/vue if (ctx.path.startsWith(‘/@modules/‘)) { const moduleName = ctx.path.substr(10) const pkgPath = path.join(process.cwd(), ‘node_modules’, moduleName, ‘package.json’) const pkg = require(pkgPath) ctx.path = path.join(‘/node_modules’, moduleName, pkg.module) } await next() })
// 1. 静态文件服务器 app.use(async (ctx, next) => { await send(ctx, ctx.path, { root: process.cwd(), index: ‘index.html’ }) await next() })
// 4. 处理单文件组件 app.use(async (ctx, next) => { if (ctx.path.endsWith(‘.vue’)) { const contents = await streamToString(ctx.body) const { descriptor } = compilerSFC.parse(contents) let code if (!ctx.query.type) { code = descriptor.script.content // console.log(code) code = code.replace(/export\s+default\s+/g, ‘const script = ‘) code += ` import { render as render } from “${ctx.path}?type=template” script.render = render export default __script ` } else if (ctx.query.type === ‘template’) { const templateRender = compilerSFC.compileTemplate({ source: descriptor.template.content }) code = templateRender.code } ctx.type = ‘application/javascript’ ctx.body = stringToStream(code) } await next() })
// 2. 修改第三方模块的路径 app.use(async (ctx, next) => { if (ctx.type === ‘application/javascript’) { const contents = await streamToString(ctx.body) // import vue from ‘vue’ // import App from ‘./App.vue’ ctx.body = contents .replace(/(from\s+[‘“])(?![.\/])/g, ‘$1/@modules/‘) .replace(/process.env.NODE_ENV/g, ‘“development”‘) } })
app.listen(3000) console.log(‘Server running @ http://localhost:3000‘)
```
