与Vue组件一起使用 如果包含一个Vue组件,它将仍然是的父组件的逻辑子组件 const app = Vue.createApp({ template: ` <h1>Root instance</h1> <parent-component /> ` }) app.component('parent-component', { template: ` <h2>This is a parent component</h2> <teleport to="#endofbody"> <child-component name="John" /> </teleport> ` }) app.component('child-component', { props: ['name'], template: ` <div>Hello, {{ name }}</div> ` })
在这种情况下,即使在不同的地方呈现child-component,它仍将是parent-componen的子组件【而不是爷爷组件】,并将从其父组件接收一个name 的props 这也意味着来自父组件的注入如预期的那样工作,并且子组件将嵌套在Vue Devtools的父组件之下,而不是放在实际内容移动到的地方 对同一目标使用多次teleports 一个常见的用例场景是一个可重用的组件,该组件可能同时有多个活动实例。对于这种场景,多个组件可以将它们的内容挂载到相同的目标元素。这个顺序将是一个简单的附加—稍后的挂载将位于目标元素中较早的挂载之后。 <teleport to="#modals"> <div>A</div> </teleport> <teleport to="#modals"> <div>B</div> </teleport> <!-- result--> <div id="modals"> <div>A</div> <div>B</div> </div>
依赖注入Provide / Inject provide 和 inject 提供依赖注入,功能类似 2.x 的 provide/inject。两者都只能在当前活动组件实例的 setup() 中调用 例如,如果我们想在根组件上提供一个book name,并将其inject到子组件上 import { provide, inject } from 'vue' const RootComponent = { setup() { provide('book', 'Vue 3 guide') } } const MyBook = { setup() { const book = inject( 'book', 'Eloquent Javascript' /* 选项的默认值,假如父组件不提供值就返回默认 */ ) return { book } } }
inject 接受一个可选的的默认值作为第二个参数。如果未提供默认值,并且在 provide 上下文中未找到该属性,则 inject 返回 undefined。 如果我们需要提供或注入多个值,我们可以通过随后分别调用provide或inject来实现【多次调用】 import { provide, inject } from 'vue' const RootComponent = { setup() { provide('book', 'Vue 3 guide') provide('year', '2020') } } const MyBook = { setup() { const book = inject( 'book', 'Eloquent Javascript' /* 选项的默认值,假如父组件不提供值就返回默认 */ ) const year = inject('year') return { book, year } } }
注入的响应性 可以使用 ref 或 reactive 来保证 provided 和 injected 之间值的响应 import { ref, reactive } from 'vue' // 提供者 setup() { const book = reactive({ title: 'Vue 3 Guide', author: 'Vue Team' }) const year = ref('2020') /*提供reactive响应式*/ provide('book', book) /*提供ref响应式*/ provide('year', year) } // 消费者 setup() { const book = inject('book') const year = inject('year') /*响应式*/ return { book, year } }
现在,当提供者组件上的book或year发生变化时,我们可以观察到它们在注入的组件上的变化。
警告:我们不建议改变一个被注入的反应性属性【子组件去修改数据流】,因为它会破坏Vue的单向数据流。相反,尝试在提供值【父组件去修改】的地方改变值,或者提供一个方法来改变值 import { ref, reactive } from 'vue' // in provider setup() { const book = reactive({ title: 'Vue 3 Guide', author: 'Vue Team' }) function changeBookName() { book.title = 'Vue 3 Advanced Guide' } provide('book', book) provide('changeBookName', changeBookName) } // in consumer setup() { const book = inject('book') const changeBookName = inject('changeBookName') return { book, changeBookName } }
指令 v-text 【Vue2.x一致】 v-html【Vue2.x一致】 v-show【Vue2.x一致】 v-if【Vue2.x一致】 v-else【Vue2.x一致】 v-else-if【Vue2.x一致】 v-for【Vue2.x一致】 v-on【Vue2.x一致】 v-bind 【Vue2.x 修饰符差异】 修饰符 .prop 去除 .sync 去除 .camel 将 kebab-case attribute 名转换为 camelCase v-model【Vue2.x一致】 v-slot【Vue2.x一致】 v-cloak【Vue2.x一致】 v-once 【Vue2.x一致】 v-pre【Vue2.x一致】 v-is【新增】 注意:本节只影响在页面的HTML中直接编写Vue模板的情况 限制:原生html元素 使用: 使用in-DOM模板时,该模板应遵守本机HTML解析规则。 某些HTML元素(例如,,和)对可以在其中显示哪些元素有限制,而某些元素(例如,和)只能 出现在某些其他元素内。 解决方法是,我们可以在这些元素上使用v-is指令【作用就是转成组件的名字】 警告v-is 功能 像一个动态2.x :is 绑定 所以要根据注册的名称渲染组件,它的值应该是一个JavaScript字符串 <!-- 不正确的, 不会出现任何渲染 --> <tr v-is="blog-post-row"></tr> <!-- 正确 --> <tr v-is="'blog-post-row'"></tr>
全局API createApp 返回一个应用程序实例,提供了一个应用程序上下文。应用程序实例挂载的整个组件树共享相同的上下文 const app = Vue.createApp({})
参数
该函数接收一个根组件选项对象作为第一个参数 const app = Vue.createApp({ data() { return { ... } }, methods: {...}, computed: {...} setup(){...} ... })
使用第二个参数,我们可以将根组件props 传递给应用 <div id="app"> <!-- 这里将会显示 'Evan' --> {{ username }} </div> const app = Vue.createApp( { props: ['username'] }, { username: 'Evan' } )
h 返回“虚拟节点”,通常缩写为VNode:一个简单的对象,它包含描述Vue应该在页面上渲染何种类型的节点的信息,包括对任何子节点的描述。你可以手动阅读render functions render() { return Vue.h('h1', {}, 'Some title') }
参数
接受三个参数tag, props and children
tag: 类型:String | Object | Function | null 详情:一个HTML标签名,一个组件,一个异步组件或null。使用null将渲染成注释。此参数是必需的 props 类型:Object 详情:模板中使用的attributes、props 和events 对应的对象。可选 children 类型: String | Array | Object 详情: Children VNodes,使用h()构建,或使用字符串来获取“text VNodes”或带有槽的对象。可选 const aaa = { props: { someProp: String }, setup(props) { console.log(props, "dsadasdasddasds"); }, render() { return h( "h2", // {Object}props //与props,attributes和events相对应的对象 //我们将在template中使用。 // 可选的。 {style: {"font-size": "20px", color: "#136"}}, [this.someProp,this.$slots.default()]); } }; app.component("anchored-heading", { render() { return h( /* // {String | Object | Function | null}标签 // HTML标记名称,组件,异步组件或null。 //使用null将渲染注释。 //必填 */ "h" + this.level, // tag name // {Object}props //与props,attributes和events相对应的对象 //我们将在template中使用。 // 可选的。 {}, // {String | Array | Object} children //使用`h()`构建的子级VNode, //或使用字符串获取“文本VNodes”或 //具有插槽的对象。 // 可选的。 [ "Some text comes first.", h("h1", "A headline"), h(aaa, { someProp: "foobar" }) ] );}, });
Vue.h( 'a', { name: headingId, href: '#' + headingId }, this.$slots.default() ) ])
限制
VNodes 必须独一无二
组件树中的所有vnode必须是唯一的。这意味着下面的渲染函数是无效的 render() { const myParagraphVNode = Vue.h('p', 'hi') return Vue.h('div', [ // 表示惊讶 - 副本复制 VNodes! myParagraphVNode, myParagraphVNode ]) }
如果您确实想多次复制相同的元素/组件,则可以使用工厂函数进行复制。 例如,以下呈现函数是呈现20个相同段落的完美有效方法: render() { return Vue.h('div', Array.apply(null, { length: 20 }).map(() => { return Vue.h('p', 'hi') }) ) }
用普通的JavaScript替换模板特性 v-if and v-for 在任何地方都可以用普通JavaScript轻松完成,Vue渲染functions 都不提供专有的替代方案。例如,在使用v-if和v-for的模板中 <ul v-if="items.length"> <li v-for="item in items">{{ item.name }}</li> </ul> <p v-else>No items found.</p> ==> props: ['items'], render() { if (this.items.length) { return Vue.h('ul', this.items.map((item) => { return Vue.h('li', item.name) })) } else { return Vue.h('p', 'No items found.') } }
v-model v-model指令被扩展到modelValue和onUpdate:modelValue道具在模板编译期间,我们将不得不自己提供这些props props: ['modelValue'], render() { return Vue.h(SomeComponent, { modelValue: this.modelValue, 'onUpdate:modelValue': value => this.$emit('update:modelValue', value) }) }
v-on 我们必须为事件处理程序提供一个适当的prop名称,例如,为了处理click事件,prop名称应该是onClick render() { return Vue.h('div', { onClick: $event => console.log('clicked', $event.target) }) }
事件修饰符 对于.passive、.capture和.once事件修饰符,Vue提供了处理程序的对象语法 render() { return Vue.h('input', { onClick: { handler: this.doThisInCapturingMode, capture: true }, onKeyUp: { handler: this.doThisOnce, once: true }, onMouseOver: { handler: this.doThisOnceInCapturingMode, //事件 once: true, //是否触发一次 capture: true }, }) }
对于所有其他事件和键修饰符,不需要特殊的API,因为我们可以在处理程序中使用事件方法 render() { return Vue.h('input', { onKeyUp: event => { // 如果发出事件的元素不存在,则中止事件绑定到的元素 if (event.target !== event.currentTarget) return // 同时如果按下的键不是enter键key (13)以及shift键没有按下 if (!event.shiftKey || event.keyCode !== 13) return // 停止事件传播 event.stopPropagation() // 阻止此元素的默认keyup处理程序 event.preventDefault() // ... } }) }
Slots 你可以访问插槽内容this.$slots在VNodes数组的 render() { // `<div><slot></slot></div>` return Vue.h('div', {}, this.$slots.default()) } props: ['message'], render() { // `<div><slot :text="message"></slot></div>` return Vue.h('div', {}, this.$slots.default({ text: this.message })) }
使用render函数将槽传递给子组件 render() { // `<div><child v-slot="props"><span>{{ props.text }}</span></child></div>` return Vue.h('div', [ Vue.h('child', {}, { // 通过`slots'作为子对象 // in the form of { name: props => VNode | Array<VNode> } default: (props) => Vue.h('span', props.text) }) ]) }
JSX 如果我们要编写大量的渲染函数,编写这样的东西可能会让人感到痛苦 Vue.h( 'anchored-heading', { level: 1 }, [Vue.h('span', 'Hello'), ' world!'] )
特别是当模板版本相比之下如此简洁的时候 <anchored-heading :level="1"> <span>Hello</span> world! </anchored-heading>
这就是为什么有一个Babel插件可以在Vue中使用JSX,让我们回到更接近模板的语法 import AnchoredHeading from './AnchoredHeading.vue' new Vue({ el: '#demo', render() { return ( <AnchoredHeading level={1}> <span>Hello</span> world! </AnchoredHeading> ) } })
defineComponent【组件】 在实现方面,defineComponent只会执行返回传递给它的对象的操作。 但是,就类型而言,返回的值具有人工渲染功能,TSX和IDE工具支持的构造函数的综合类型 参数 具有组件选项的对象 import { defineComponent } from 'vue' const MyComponent = defineComponent({ data() { return { count: 1 } }, methods: { increment() { this.count++ } } })
defineAsyncComponent 【异步组件】 创建只在必要时加载的异步组件 参数 对于基本用法,defineAsyncComponent可以接受返回Promise的工厂函数。当您从serve检索到组件定义时,应该调用Promise的解析回调。您还可以调用reject(reason)来指示加载失败。 import { defineAsyncComponent } from 'vue' const AsyncComp = defineAsyncComponent(() => /*或者*/ import('./components/AsyncComponent.vue') /*或者*/ new Promise((resolve, reject) => { /*可以reject*/ resolve({ template: '<div>I am async!</div>' }) }) ) app.component('async-component', AsyncComp)
在使用本地注册时,还可以直接提供返回Promise的函数 import { createApp, defineAsyncComponent } from 'vue' createApp({ // ... components: { AsyncComponent: defineAsyncComponent(() => import('./components/AsyncComponent.vue') ) } })
对于高级用法,defineAsyncComponent可以接受一个对象 const AsyncComp = defineAsyncComponent({ // 工厂函数 loader: () => import('./Foo.vue') // 加载异步组件时使用的组件 loadingComponent: LoadingComponent, //加载失败的时候使用的组件 errorComponent: ErrorComponent, // 在显示加载组件之前延迟。默认值:200 ms。 delay: 200, // 如果超时,将显示错误组件 // 存在timeout并且超过这个时间. 默认值:无穷 timeout: 3000, // 返回布尔值的函数,指示当加载器promise rejects时异步组件是否应该重试 retryWhen: error => error.code !== 404, // 允许的最大重试次数 maxRetries: 3, // 定义组件是否可挂载 suspensible: false })
resolveComponent
警告resolveComponent只能在render或setup函数中使用。 允许通过名称解析组件,如果它在当前应用程序实例中可用。如果找不到组件,返回组件或未定义组件 如果找不到组件,返回组件或未定义组件【组件】 app.component('MyComponent', { /* ... */ }) const MyComponent = resolveComponent('MyComponent')
resolveDynamicComponent【解析活动的组件active】
resolveDynamicComponent只能在render或setup函数中使用。 允许使用与<component:is="">相同的机制来解析组件。 返回解析的组件或一个新创建的VNode以组件名称作为节点标记的。 如果没有找到组件,会发出警告 resolveDirective 警告resolveDirective只能在render或setup函数中使用。 允许通过名称解析指令,如果它在当前应用程序实例中可用。 返回一个Directive或 当没有找到的时候,返回undefined。 app.directive('highlight', {}) render(){ const highlightDirective = resolveDirective('highlight') }
withDirectives
警告withDirectives只能在render或setup函数中使用。 :::允许应用指令到VNode。返回一个带有应用指令的VNode。 const bar = resolveDirective('bar') return withDirectives(h('div'), [ [bar, this.y] ])
createRenderer *【待】 nextTick 将回调延迟到下一个DOM更新周期之后执行。在更改了一些数据以等待DOM更新之后立即使用它 setup() { const message = ref('Hello!') const changeMessage = async newMessage => { message.value = newMessage /*等待DOM更新*/ await nextTick() console.log('Now DOM is updated') } }
实例方法methods $watch 参数 {string | Function} source {Function | Object} callback {Object} [options] {boolean} deep {boolean} immediate 用法 观察组件实例上的响应式属性或computed函数的更改。使用回调获取到给定属性的新值和旧值。我们只能通过顶级data、prop或computed的属性名作为字符串的形式传递。对于更复杂的表达式或嵌套属性,使用函数代替。 例子 const app = Vue.createApp({ data() { return { a: 1, b: 2, c: { d: 3, e: 4 } } }, created() { // 顶级属性名a this.$watch('a', (newVal, oldVal) => { // 做一些事 }) // 观察监视单个嵌套属性 this.$watch( () => this.c.d, (newVal, oldVal) => { // 做一些事 } ) // 监控复杂表达式 this.$watch( // 每当表达式`this.a + this.b`产生不同的结果时 // 处理程序将被调用。这就好像我们在看computed属性 // 而不定义计算属性本身 () => this.a + this.b, (newVal, oldVal) => { // 做一些事 } ) } })
当监视的值是对象或数组时,对其属性或元素的任何更改都不会触发监视程序,因为它们引用相同的对象/数组 const app = Vue.createApp({ data() { return { article: { text: 'Vue is awesome!' }, comments: ['Indeed!', 'I agree'] } }, created() { this.$watch('article', () => { console.log('Article changed!') }) this.$watch('comments', () => { console.log('Comments changed!') }) }, methods: { // 这些方法不会触发观察者,因为我们仅更改了对象/数组的属性, // 并不是 Object/Array 本身 changeArticleText() { this.article.text = 'Vue 3 is awesome' }, addComment() { this.comments.push('New comment') }, // 这些方法会触发观察者,因为我们完整替换了对象/数组 changeWholeArticle() { this.article = { text: 'Vue 3 is awesome' } }, clearComments() { this.comments = [] } } })
$watch返回一个取消监视的函数,该函数停止触发回调 const unwatch = vm.$watch('a', cb) // later, teardown the watcher unwatch()
Option: deep 检测对象内部嵌套的值更改,需要在options参数中传入deep: true。注意,侦听数组突变并不需要这样做。 vm.$watch('someObject', callback, { deep: true }) vm.someObject.nestedValue = 123 // 触发回调
Option: immediate 在选项中传入immediate: true将立即用表达式的当前值触发回调 vm.$watch('a', callback, { immediate: true }) // “callback”被立即触发,当前值为“a”
请注意,使用immediate选项,您将无法在第一个回调调用中取消监视给定的属性。 //这个例子是错误的 const unwatch = vm.$watch( 'value', function() { doSomething() unwatch() }, { immediate: true } )
如果你仍然想在回调中调用一个unwatch函数,你应该首先检查它的可用性 const unwatch = vm.$watch( 'value', function() { doSomething() if (unwatch) { unwatch() } }, { immediate: true } )
$emit 【一致】 $forceUpdate【一致】 $nextTick【一致】 实例 property vm.$data 【一致】 vm.$props 【一致】 vm.$el 【一致】 vm.$options 【一致】 vm.$parent 【一致】 vm.$root【一致】 vm.$slots 【一致】 vm.$refs 【一致】 vm.$attrs 【一致】 废弃: vm.$children vm.$slots vm.$scopedSlots vm.$isServer vm.$listeners 选项 / 组合 mixins 【一致】 extends【一致】 provide / inject【一致】 parent【废弃】 setup【新增】 详情见上 选项 / 资源 directives【一致】 components【一致】 filters【废弃】 选项 / 数据 data【一致】 props【一致】 computed【一致】 methods【一致】 watch【一致】 emits【新增】 详情 可以从组件发出的自定义事件的list/hash。 它具有基于数组的简单语法和允许配置事件验证的替代的基于对象的语法。 在基于对象的语法中,每个属性的值可以为null或验证函数。 验证函数将接收传递给emit调用的其他参数。例如,如果调用this.emit调用的其他参数。 例如,如果调用this.emit调用的其他参数。例如,如果调用this.emit('foo',1),则foo的相应验证器将接收参数1。验证器函数应返回一个布尔值,以指示事件参数是否有效。 const app = Vue.createApp({})
// 数组语法 app.component('todo-item', { emits: ['check'], created() { this.$emit('check') } }) // 对象语法 app.component('reply-form', { emits: { // 无效 click: null, // 有效 submit: payload => { if (payload.email && payload.password) { return true } else { console.warn(`Invalid submit event payload!`) return false } } } })
提示 在emit选项中列出的事件将不会被组件的根元素继承。
vue都是函数
createApp const app = createApp(App) app.use(store) app.use(router) app.mount('#app')
传了两个属性
v-model:selectKeys = "selectKeys"
import {reactive,toRef } from 'vue export default{ setup(props,ctx){ //默认执行一次 //页面使用 state.selectKeys const state = reactive({ //attr slots emit selectKeys:0 }) //1.直接使用 return { selectKeys:state.selectKeys } //2.导出,页面上直接使用,数据响应式还带解构 return { ...toRefs(state) } onMounted(()=>{ }) } }
监控路由变化
import {reactive,toRef,watch } from 'vue import {useRoute} from 'vue-router' export default{ setup(props,ctx){ const state = reactive({ //attr slots emit selectKeys:0 }) //1.watch监控路由变化 watch(()=>route.path,(newValue)=>{ state.selectKeys = [newValue] }) //2.computed监控路由变化 const selectKeys = computed(()=>{ return [route.path] }) return { selectKeys } } }
vuex
import {reactive,toRef,watch ,computed} from 'vue' import {useRoute} from 'vue-router' export default{ setup(props,ctx){ const route = userRoute() const store = useStore() const state = reactive({ //attr slots emit selectKeys:0 }) //1.watch监控路由变化 watch(()=>route.path,(newValue)=>{ state.selectKeys = [newValue] }) //2.computed监控路由变化 const selectKeys = computed(()=>{ return [route.path] }) //ref 把普通值变成包装后的结构,将属性变成响应式 // ref(store.getters.allTime) return { selectKeys, allTime:ref(store.getters.allTime) } } } //store.js import {createStore} from 'vuex export default { state:{ }, getters:{ allTime:()=>{ return 0 } }, mutations:{ }, actions:{ }, modules:{ } }
组件通信
import {reactive,toRef,watch ,computed} from 'vue' import {useRoute} from 'vue-router' import moment from 'moment' export default{ setup(props,ctx){ const state = reactive({ form:{ date:moment(Date.now()).format('YYYY-MM-DD') } }) //方法函数 const onSubmit =()=>{ //传给父组件 this.$emit('handlePlan',state.form) } return { ...toRefs(state), onSubmit } } } //父组件 <Child @handlePlan="handlePlan" /> import {reactive,toRef,watch ,computed} from 'vue' import {useRoute} from 'vue-router' import moment from 'moment' export default{ setup(props,ctx){ const state = reactive({ form:{ date:moment(Date.now()).format('YYYY-MM-DD') } }) const handlePlan = (plan)=>{ console.log(plan) } return { handlePlan } } }
环境变量
VUE_APP_URL = 'http://www.xxx.com:3000'
封装api
import axios from 'axios const instance = axios.create({ baseURL:process.env.VUE_APP_URL, timeout:3000 }) instance.interceptors.request.use((config)=>{ return config }) instance.interceptors.response.use((res)=>{ return res.data.data },err=>{ return Promise.reject(err) }) export function request(opts){ return instance(opts) } //request.js import {request } from '../utils/axios' export function getPlanList(){ return request({url:'/plan',method:'get'}) } export function addPlan(data){ return request({url:'/plan',method:'post',data}) } export function deletePlan(){ return request({url:'/plan',method:'delete',params:{id}}) } //action_type.js export const SET_PLAN_LIST = 'SET_PLAN_LIST' export const ADD_PLAN = 'ADD_PLAN' export const DELETE_PLAN = 'DELETE_PLAN' //store.js import {createStore} from 'vuex' export * as types from './action_type' import * as api from './request' export default { state:{ }, getters:{ allTime:()=>{ return 0 } }, mutations:{ [type.ADD_PLAN](state,payload){ state.planList = [...state.planList,payload] }, [type.DELETE_PLAN](state,payload){ state.planList.filter(item=>{ return item._id !=payload._id }) }, [type.SET_PLAN_LIST](state,payload){ }, }, actions:{ //restful api根据不同方法返回不同的资源 async [type.ADD_PLAN]({commit},payload){ let plan = await api.addPlan(payload) commit(type.ADD_PLAN,plan) }, async [type.DELETE_PLAN]({commit},payload){ let plan = await api.deletePlan(payload) commit(type.DELETE_PLAN,plan) }, async [type.SET_PLAN_LIST]({commit},payload){ let plan = await api.getPlanList(payload) commit(type.SET_PLAN_LIST,plan) }, }, modules:{ } }
使用数据
import {reactive,toRef,watch ,onMounted,onUpdated,compile,computed} from 'vue' import {useStore} from 'vuex' import moment from 'moment' import * as types from '@/store/action_types' export default{ setup(props,ctx){ const store = useStore() // const state = reactive({ // planList:store.state.planList //这样取的是默认值 // }) onMounted(()){ store.dispatch(types.SET_PLAN_LIST) } //时间格式化方法 const formatDate = (value)=>{ return moment(value).format('YYYY-MM-DD') } return { ...toRefs(state.store), formatDate } } }
简版vue
//1.创建虚拟节点,将虚拟节点转化为真实节点 //2.组件的实现 setup //3.reactive api实现effect //4.diff算法 //5.vite let { render} = Vue const state = { count:0 } const vnode = { tag:'div', props:{color:'red'}, children:[ { tag:'p', props:{color:'blue}, children:[ 'vue@3-计数器' ] }, { tag:'p', props:{ onClick:()=>{ alert(state.count) } } children:[ 'vue@3-计数器' ] } ] } render(vnode,app) export function render(vnode,container){ // 渲染页面的方法叫patch //1.第一次渲染 2.dom-diff patch(null,vnode,container) } /** * n1 老的虚拟节点 * n2 新的虚拟节点 * container 容器 */ function patch(n1,n2,container){ //组件的虚拟节点是一个对象,tag是一个对象 //如果是组件,tag可能是个对象 //后续diff可以执行这个方法 if(typeof n2.tag ==='string'){ //标签 mountElement(n2,container) }else if(typeof n2.tag==='object'){ } } function mountElement(vnode,container){ const { tag,children,props } = vnode //虚拟节点和真实节点做映射关系 let el = (vnode.el = nodeOps.createElement(tag)) if(Array.isArray(children)){ mountChild(children,el) }else{ nodeOps.hostSetElementText(el,children) } container.insert(el,container) } function mountChild(children,container){ for(var i=0;i<children.length;i++){ let child = children[i] patch(null,child,container) } } //节点操作方法 exoprt const nodeOps = { //插入元素节点 insert(child,parent,anchor){ if(anchor){ parent.insertBefore(child,anchor) }else{ parent.appendChild(child) } }, //移除节点 remove(child){ const parent = child.parentNode; parent && parent.removeChild(child) }, //创建节点 createElement(tag){ return document.createElement(tag) }, //设置文本内容 hostSetElementText(el,text){ el.textContent = text } }