Skip to content

组件的使用与使用过滤器(Lesson8 1/3) ​

本页对应 Lesson08,共 6 段视频,约 1 小时 36 分。

学习目标 ​

  • 能使用 render 函数通过 h/createElement 创建并挂载虚拟节点,说明 template 与 render 的优先级及执行效率差异。
  • 能使用 Vue CLI 创建并配置脚手架项目,完成依赖安装、项目启动,以及端口、主机、自动打开浏览器等开发服务配置。
  • 能在脚手架中创建主组件和子组件,完成组件引入、注册、父子属性传递、事件绑定及 scoped 样式隔离。
  • 能定义并使用局部过滤器与全局过滤器,并通过 Vue.use 以对象 install 或函数形式将过滤器注册为插件。
  • 能基于给定页面结构和数据,在脚手架中完成 todoList 的界面渲染,并正确引入全局样式或组件内样式。

知识点 ​

入口文件:用 render 指定主组件 ​

为什么需要:脚手架项目启动后,必须告诉 Vue 把哪个组件渲染到 #app 上。转写中反复调试入口文件,删掉 src 后会提示找不到入口,说明入口是项目运行的起点。
怎么想:入口不直接写一堆模板,而是引入最外层的主组件 App,通过 render 返回它。render 接收的 h 是创建元素/组件的函数。
怎么做:

js
import Vue from "vue";
import App from "@/App";
new Vue({
	el:"#app",
	render:h=>h(App)
})

易错点:h(App) 里的 App 是组件对象,不是字符串;@ 通常代表 src。转写中还演示过:在脚手架入口直接写 template 当前版本会报警/不生效,所以入口用 render 更稳。若不用 el,需要手动挂载。

App.vue:结构层、逻辑层、样式层 ​

为什么需要:一个 .vue 文件要把结构、逻辑、样式放在一起,便于维护。转写中删掉结构层后页面没有内容,说明模板不能省。
怎么想:template 是结构层,必须有且只能有一个根元素;script 写逻辑;style 写样式。
怎么做:

vue
<!--
    结构层-不允许省略
    有且只能是一个根元素
-->
<template>
    <div>
        <h3 @click="num++">APP:{{num}}</h3>
        <hr/>
        <Child :num="num"></Child>
    </div>
</template>

<!--逻辑层-->
<script>
import Child from "@/components/Child";
export default {
    name: "App.vue",
    data(){
        return {
            num:1
        }
    },
    components:{
        Child
    }
}
</script>

<!--样式层-->
<style lang="less" scoped>
    div{
        h3{
            color:red;
        }
    }

</style>

易错点:模板里写两个同级 div 会编译失败,转写中就是加一个外层 div 包裹后解决;template 不能省略,script、style 可省。lang="less" 需要项目支持 less。

子组件注册与使用:import + components + 标签 ​

为什么需要:页面要拆分,App 作为主组件,Child 作为子组件。
怎么想:先 import Child,再在 components 中注册,最后模板里写 <Child>。转写中只引入不注册就不能用。
怎么做:App 中已写 import Child from "@/components/Child"; 和 components:{ Child },模板写 <Child :num="num"></Child>。
易错点:components 拼写;注册名与标签名对应;@/components/Child 指向 src/components/Child.vue。VS Code 中可用提示自动引入,但要看路径是否正确。

props 传值与校验:父传子 ​

为什么需要:App 的 num 要显示在 Child 中,不能直接改子组件内部数据,要用 props 接收。
怎么想:父组件用 :num="num" 传变量;子组件声明 props 接收。转写中不声明接收时控制台报错/不显示。
怎么做:

vue
<template>
    <div>
        <h3>Child:{{num}}</h3>
    </div>
</template>

<script>
export default {
    name: "Child",
    // props:["num"],
    props:{
        num:{
            type:Number,
            default:100
        }
    },
    beforeCreate() {
        console.log("1-beforeCreate");
    },
    created(){
        console.log("2-created");
    },
    beforeMount() {
        console.log("3-beforeMount");
    },
    mounted(){
        console.log("4-mounted");
    },
    beforeUpdate() {
        console.log("1-beforeUpdate");
    },
    updated(){
        console.log("2-updated");
    }

}
</script>

<style scoped>
    h3{
        color:green;
    }
</style>

易错点::num="num" 传的是变量,num="num" 传的是字符串;type:Number 与默认值 100 要匹配;不传时默认值生效。转写中演示过去掉不合适的校验后页面正常。

scoped 样式作用域:解决父子样式冲突 ​

为什么需要:转写中 App 的 h3 红色把 Child 的 h3 绿色覆盖了,刷新后看到红色,鼠标查看子组件样式被划掉。
怎么想:给组件样式加 scoped,让样式只作用于当前组件模板。Vue 会给元素加类似 data-v-xxx 的属性,选择器带属性后就不互相覆盖。
怎么做:Child 的 <style scoped> 已加;App 的 <style lang="less" scoped> 也加。
易错点:不加 scoped 容易全局污染;scoped 只解决当前组件模板范围,不是所有深层样式都随便穿透。

生命周期观察:父数据变化触发子组件更新 ​

为什么需要:理解组件创建、挂载、更新顺序,调试时知道日志为什么这样打。
怎么想:Child 中已有钩子日志。首次渲染会依次看到 beforeCreate → created → beforeMount → mounted;点击 App 的 h3 让 num++ 后,父数据变化传到子组件,会触发 beforeUpdate → updated。
怎么做:打开控制台,点击 APP:{{num}} 观察 Child 的日志。
易错点:beforeCreate/created 只执行一次;更新阶段不是重新 created;日志要写在 Child 中才能观察子组件更新。

先把问题定位清楚:过滤器为什么要分“局部”和“全局” ​

这一节的核心不是重新讲一遍管道符,而是解决一个实际报错:在 App.vue 里定义好的过滤器,放到子组件里使用时不生效。转写稿里先在主组件中写了商品价格、上架时间,用 {{ item.price | currency }}、{{ item.time | d }} 展示;随后在子组件里也想用 d,结果报错。原因不是写法坏了,而是过滤器注册位置不对:在哪个组件里注册,就只允许哪个组件使用,这叫局部过滤器。需要跨组件复用时,就要注册成全局过滤器。

局部过滤器:在哪个组件注册,就在哪个组件用 ​

先在主组件里准备数据,用 v-for 渲染商品列表,再用管道符格式化价格和时间:

html
<div v-for="item in goodsList" :key="item.id">
  <p>商品名称:{{ item.name }}</p>
  <p>商品价格:{{ item.price | currency }}</p>
  <p>上架时间:{{ item.time | d }}</p>
</div>
js
export default {
  data() {
    return {
      goodsList: [
        { id: 1, name: '商品一号', price: 19.123, time: '...' },
        { id: 2, name: '商品二号', price: 456, time: '...' }
      ]
    }
  },
  filters: {
    currency(value, n) {
      // 转写稿中保留了原过滤器实现,这里不重复编造
    },
    d(value) {
      // 时间过滤器实现保持不变
    }
  }
}

想保留三位小数时,通过括号传参:

html
{{ item.price | currency(3) }}

这里要记住:管道符左边的数据是过滤器的第一个参数,括号里的 3 是第二个参数。易错点是:在 App.vue 的 filters 里注册后,子组件 Chart 直接写 {{ t | d }} 会报“找不到过滤器”,因为局部过滤器不会自动下发给子组件。

全局过滤器的三种注册方式 ​

第一种最直接,在 main.js 里用 Vue.filter 注册:

js
import Vue from 'vue'
import App from './App.vue'

Vue.filter('d', function (value) {
  // 原时间过滤器实现
})
Vue.filter('currency', function (value, n) {
  // 原价格过滤器实现
})

new Vue({
  render: h => h(App)
}).$mount('#app')

第二种是把过滤器集中到 src/filters/index.js,导出对象,再在 main.js 里循环注册:

js
// src/filters/index.js
export default {
  d(value) { /* ... */ },
  currency(value, n) { /* ... */ }
}
js
// main.js
import Vue from 'vue'
import App from './App.vue'
import filters from './filters'

for (let key in filters) {
  Vue.filter(key, filters[key])
}

new Vue({
  render: h => h(App)
}).$mount('#app')

第三种是放到 beforeCreate 里,在创建实例前完成注册:

js
new Vue({
  beforeCreate() {
    for (let key in filters) {
      Vue.filter(key, filters[key])
    }
  },
  render: h => h(App)
}).$mount('#app')

易错点:忘记 import filters、过滤器重名、循环变量写错,都会导致“页面没报错但格式没变”。注册时机要保证在组件渲染前。

把过滤器做成插件:Vue.use 到底做了什么 ​

如果每次都在 main.js 里写循环,重复且容易漏。转写稿把它封装成插件,用 Vue.use(filters) 安装。Vue.use 接收插件时有两种情况:如果传入的是对象,就调用对象里的 install 方法,并把 Vue 传进去;如果传入的是函数,就直接调用这个函数,并把 Vue 传进去。对象写法:

js
const filters = {
  d(value) { /* ... */ },
  currency(value, n) { /* ... */ }
}

export default {
  install(Vue) {
    for (let key in filters) {
      Vue.filter(key, filters[key])
    }
  }
}
js
// main.js
import Vue from 'vue'
import App from './App.vue'
import filters from './filters'

Vue.use(filters)

new Vue({
  render: h => h(App)
}).$mount('#app')

函数写法更简洁:

js
const filters = { /* ... */ }

export default function (Vue) {
  for (let key in filters) {
    Vue.filter(key, filters[key])
  }
}

易错点:export default 导出的对象如果没有 install,Vue.use 不会帮你注册;install 里拿到的是 Vue 构造函数,不是组件实例;插件写法本质仍是注册全局过滤器。

接到 TodoList 页面时,别把过滤器当成组件 ​

转写稿最后开始搭 TodoList:把页面模板复制到 App.vue,在 main.js 里引入 App 并用 render: h => h(App) 挂载;样式可以放进组件的 style,也可以新建 src/assets/css/index.css,在 script 里 import 引入。这里要区分:过滤器负责“格式化显示”,组件负责“结构和逻辑”。易错点是模板根元素、样式作用域和 scoped 的使用;过滤器仍然按“局部注册只在当前组件可用,全局注册才跨组件可用”的规则处理。

先明确本节目标:把 todoList 的静态界面渲染出来 ​

为什么需要:todoList 后面要接增加、删除、全选、反选和缓存,如果一上来就写逻辑,页面结构和入口文件容易混在一起。先把“壳”渲染出来,后面才有稳定的调试基础。

怎么想:转写稿的思路是先从已有 todoList 页面里复制 id="root" 所包裹的 div 结构,放进 App.vue 的模板;再新建入口 main.js,把 App.vue 挂到 #app 上;样式可以复制进组件的 style,也可以放到 src/assets/css/index.css 再引入。

怎么做:入口文件写成下面这样,render 返回主组件 App,最后手动挂载:

js
import Vue from 'vue'
import App from './App.vue'
import './assets/css/index.css'

new Vue({
  render: h => h(App)
}).$mount('#app')

主组件先保留三段式骨架:

vue
<template>
  <div>
    <!-- 这里放从 todoList 页面复制来的结构 -->
  </div>
</template>

<script>
export default {
  name: 'App'
}
</script>

<style scoped>
/* 也可以把样式放到 assets/css/index.css 中引入 */
</style>

易错点:挂载点要和 public/index.html 里的 id="app" 对应;样式放 assets/css/index.css 时,入口里要用 import 引入,@ 在脚手架中通常代表 src。转写稿里反复强调“先呈现界面,再加功能”,不要在这一步提前写增删改查。

render 是脚手架入口文件的写法 ​

为什么需要:脚手架入口没有直接写 el 和 template,而是用 render 指定主组件。不理解它,就看不懂 main.js 为什么是 h => h(App)。

怎么想:render 接收一个创建节点的函数,通常命名为 h;h(App) 表示用 App 组件生成虚拟节点,最终替换挂载点内容。转写稿中先用 h('div', '你好') 演示,再改成 h(App),就是为了说明“简单结构可以直接写,复杂页面要交给组件”。

怎么做:入口统一用:

js
new Vue({
  render: h => h(App)
}).$mount('#app')

易错点:template 和 render 同时存在时,render 优先级更高;转写稿里在脚手架中直接写 template 会报“当前版本不支持这种直接渲染”,所以不要照搬旧写法。render 里写 h('div', '你好') 只适合演示,真实页面应返回组件。

单文件组件与样式作用域 ​

为什么需要:todoList 有结构、逻辑和样式,单文件组件把三部分放在一个 .vue 文件里,主组件和子组件职责更清楚。

怎么想:App.vue 是最外层主组件,子组件放 components 目录;模板负责结构,script 写数据和方法,style 写样式。转写稿中演示两个 H3 样式互相覆盖,解决方式是给 style 加 scoped,或者用 lang="less" 并加作用域。

怎么做:组件模板只能有一个根节点;样式冲突时写:

vue
<style scoped>
h3 { color: red; }
</style>

需要 Less 时写 <style lang="less" scoped>。易错点:template 不能省略,script 和 style 可以省略;模板出现两个同级根节点会编译失败,要先套一个外层 div;scoped 会给元素加 data-v-* 属性,使样式只作用当前组件。

过滤器:局部与全局,以及作为插件安装 ​

为什么需要:todoList 后面会涉及价格、时间等显示格式,过滤器可以把格式化逻辑从模板里抽出来。

怎么想:在某个组件里写的 filters 是局部过滤器,只在该组件模板中可用;多个组件都要用,就注册全局过滤器,或把过滤器做成插件,用 Vue.use() 安装。

怎么做:局部写法是在组件里写 filters: { ... };全局可以用 Vue.filter('currency', fn)。转写稿中把过滤器放到 src/filters/index.js,并导出插件:

js
const filters = {
  // 这里放前面写好的过滤器函数
}

export default {
  install(Vue) {
    for (let key in filters) {
      Vue.filter(key, filters[key])
    }
  }
}

入口中安装:

js
import Vue from 'vue'
import App from './App.vue'
import filters from './filters'

Vue.use(filters)

new Vue({
  render: h => h(App)
}).$mount('#app')

易错点:Vue.use(filters) 时,如果 filters 是对象,对象里必须有 install 方法,否则转写稿中会出现“过滤器没生效、控制台报错”;如果导出的是函数,Vue.use 会直接调用该函数并传入 Vue。局部过滤器只在注册它的组件里有效,换到子组件就用不了,这是转写稿中专门调试过的点。

渲染 todoList 页面:先静态,后数据 ​

为什么需要:todoList 的完整功能依赖列表数据,但本节转写稿只做到“把页面结构渲染出来、样式引入成功”,所以要先保证静态页面可见,再逐步接 v-for、事件和缓存。

怎么想:先把复制来的 todoList 结构放进 App.vue,样式可以走两条路:一是直接放进组件 style;二是放到 src/assets/css/index.css,在入口或组件中 import。转写稿最后验证的是页面能正常显示,而不是一次性写完所有交互。

怎么做:保留 App.vue 作为主组件,入口用 render 挂载;后续列表数据放进 data() 返回的对象,用 v-for 渲染,key 用唯一 id。本节先不要凭空补全增删改查代码,转写稿明确说这些功能后面再做。

易错点:data 在组件中必须写成函数并返回对象;v-for 的 key 不要用索引凑合;样式文件路径不要写错。转写稿的调试结论是:先把静态壳和样式跑通,再进入 todoList 的交互逻辑,否则报错时很难判断是入口、组件还是样式的问题。

课堂画面 ​

课堂画面 1

课堂画面 2

课堂画面 3

课堂画面 4

课堂画面 5

课堂画面 6

代码示例 ​

示例一:脚手架中组件的使用(main.js + App.vue + Child.vue) ​

js
// src/main.js
import Vue from "vue";
import App from "@/App";

new Vue({
  el: "#app",
  render: h => h(App)
});
vue
<!-- src/App.vue -->
<!--
    结构层-不允许省略
    有且只能是一个根元素
-->
<template>
  <div>
    <h3 @click="num++">APP:{{ num }}</h3>
    <hr />
    <Child :num="num"></Child>
  </div>
</template>

<!--逻辑层-->
<script>
import Child from "@/components/Child";
export default {
  name: "App.vue",
  data() {
    return {
      num: 1
    };
  },
  components: {
    Child
  }
};
</script>

<!--样式层-->
<style lang="less" scoped>
div {
  h3 {
    color: red;
  }
}
</style>
vue
<!-- src/components/Child.vue -->
<template>
  <div>
    <h3>Child:{{ num }}</h3>
  </div>
</template>

<script>
export default {
  name: "Child",
  // props:["num"],
  props: {
    num: {
      type: Number,
      default: 100
    }
  },
  beforeCreate() {
    console.log("1-beforeCreate");
  },
  created() {
    console.log("2-created");
  },
  beforeMount() {
    console.log("3-beforeMount");
  },
  mounted() {
    console.log("4-mounted");
  },
  beforeUpdate() {
    console.log("1-beforeUpdate");
  },
  updated() {
    console.log("2-updated");
  }
};
</script>

<style scoped>
h3 {
  color: green;
}
</style>

关键行与易错点

  • render: h => h(App):脚手架入口不用 template,而是用 render 函数把主组件 App 渲染出来;el: "#app" 对应 public/index.html 里 <div id="app"> 的挂载点。若把 el 写成 #root 之类不存在的选择器,页面就是空白。
  • template 与 render 同时存在时,template 优先级更低,最终仍由 render 生成虚拟节点——这也是入口文件统一写 render 的原因。
  • <template> 里有且只能有一个根元素。把两个 div 并列写在模板顶层会直接编译失败,必须先包一层外层 div。
  • 结构层(template)不允许省略,script 与 style 都可以省。
  • data 在组件里必须是函数并 return 一个对象,写成对象形式会导致多个实例共享同一份数据。
  • 父传子:<Child :num="num"> 中的 : 是 v-bind 简写,不加冒号传的是字符串 "num" 而不是变量值。子组件用 props 接收,对象写法可声明 type、default;若父组件传了值又设了 default,default 不生效;type: Number 时传字符串会在控制台报警告。
  • scoped:不加 scoped 时父子同名选择器(都写 h3)会互相覆盖,父组件的红色样式会把子组件的绿色干掉;加上 scoped 后 Vue 会给元素加 data-v-xxx 属性选择器来隔离作用域。使用 less 需要在 <style> 上写 lang="less"。
  • 生命周期执行顺序:父 beforeCreate → created → beforeMount → 子 beforeCreate → created → beforeMount → mounted → 父 mounted;父组件数据变化只会触发子组件的 beforeUpdate / updated,不会再走 created、mounted。

示例二:把过滤器作为插件安装(全局过滤器) ​

js
// src/filters/index.js
// 导出一个函数:Vue.use() 时会被直接调用,并收到 Vue 构造函数
export default function (V) {
  let filters = {
    // 价格过滤器:第二个参数是保留的小数位数
    currency(value, n = 2) {
      return "¥" + Number(value).toFixed(n);
    },
    // 时间过滤器
    d(value) {
      let date = new Date(value);
      let y = date.getFullYear();
      let m = ("0" + (date.getMonth() + 1)).slice(-2);
      let day = ("0" + date.getDate()).slice(-2);
      return `${y}-${m}-${day}`;
    }
  };

  // 遍历过滤器对象,逐个注册成全局过滤器
  for (let key in filters) {
    V.filter(key, filters[key]);
  }
}
js
// src/main.js
import Vue from "vue";
import App from "@/App";
import filters from "@/filters";

// 安装插件:传入的是函数 → 直接调用该函数并传入 Vue
Vue.use(filters);

new Vue({
  el: "#app",
  render: h => h(App)
});

关键行与易错点

  • Vue.use(插件) 干了什么:插件是对象时,调用对象里的 install(Vue) 方法;插件是函数时,直接调用该函数并把 Vue 传进去。示例里 filters 是函数,所以 Vue.use(filters) 等价于执行 filters(Vue)。
  • 函数参数 V 就是 Vue 构造函数本身,因此函数内部可以省掉 import Vue from "vue",直接用 V.filter(...) 注册。
  • 如果只 export default { currency(){}, d(){} } 而没有 install 方法,Vue.use 什么都不会执行,页面上会报「找不到过滤器」,这是最常见的坑。
  • 局部过滤器写在某个组件的 filters 选项里,只在当前组件生效;在子组件(如 Child.vue)里用 {{ t | d }} 会报错,必须像本例这样注册成全局过滤器。
  • 注册时机:Vue.use 要写在 new Vue(...) 之前(或写在 beforeCreate 里),否则实例创建时过滤器还没注册。
  • 模板中过滤器写法:{{ price | currency }} 冒号左边是被处理的数据,括号里传的是第二个参数,如 {{ price | currency(3) }}。

示例三:列表渲染 + 过滤器(todoList / 商品列表界面) ​

vue
<!-- src/App.vue -->
<template>
  <div>
    <h3 @click="num++">APP:{{ num }}</h3>
    <hr />
    <ul>
      <li v-for="item in goodsList" :key="item.id">
        <span>商品名称:{{ item.goodsName }}</span>
        <span>价格:{{ item.price | currency(3) }}</span>
        <span>上架时间:{{ item.time | d }}</span>
      </li>
    </ul>
  </div>
</template>

<script>
export default {
  name: "App",
  data() {
    return {
      num: 1,
      goodsList: [
        { id: 1, goodsName: "商品一号", price: 19.123, time: Date.now() },
        { id: 2, goodsName: "商品二号", price: 456, time: Date.now() }
      ]
    };
  }
};
</script>

<style scoped>
ul {
  list-style: none;
}
li {
  height: 40px;
  line-height: 40px;
}
</style>

关键行与易错点

  • v-for="item in goodsList" 必须配 :key,这里用数据里唯一的 item.id,不要用 index 当 key(后续做删除、选中、全选反选时会出现错乱)。
  • 过滤器只能写在插值表达式 {{ }} 或 v-bind 里,{{ item.price | currency(3) }} 中的 3 是传给过滤器的第二个参数,第一个参数永远是被过滤的数据本身。
  • 过滤器里的 value 是原始数据,函数必须 return 处理结果,只做打印不返回,页面会显示空。
  • 静态资源与全局样式建议放到 src/assets(图片)和 src/assets/css/index.css,在 script 里 import "@/assets/css/index.css" 引入;另一种做法是把页面样式直接抄进 <style> 里,两者效果相同,区别是前者是全局样式、后者受 scoped 限制。
  • 列表数据变化(新增、删除、勾选)时只会触发 beforeUpdate / updated,不会重新走 created、mounted,需要缓存的逻辑要写在 updated 或对应的操作函数里。

常见错误 ​

  • 现象:App.vue 的 <template> 下写两个同级 <div>,编译直接失败。→ 原因:模板结构层有且只能有一个根元素。→ 修法:外层补一个包裹容器,把同级节点收进去。
  • 现象:Child.vue 里 h3 被父组件 App.vue 的红色样式覆盖。→ 原因:父组件样式未隔离,选择器命中了子组件节点。→ 修法:父、子组件样式都加 scoped;需要穿透时再单独处理。
  • 现象:父组件传 :num="num",子组件收到字符串或默认值不生效。→ 原因:props 声明方式与传入值不匹配,或漏写 type/default。→ 修法:用对象形式声明 props,明确 type、default,动态绑定保留冒号。
  • 现象:子组件里直接改 props.num,控制台警告且数据流混乱。→ 原因:props 是单向数据流,子组件不应直接修改。→ 修法:子组件用 data/computed 接收派生值,或通过 $emit 通知父组件修改。
  • 现象:入口文件同时写 template 和 render,页面显示的不是预期模板。→ 原因:render 优先级高于 template。→ 修法:入口统一用 render: h => h(App),不要在入口再写 template。
  • 现象:局部过滤器在另一个组件中使用时报“未定义”。→ 原因:局部过滤器只在注册它的组件作用域内有效。→ 修法:改为全局过滤器,或通过插件安装统一注册。
  • 现象:Vue.use(filters) 后过滤器仍不生效。→ 原因:插件导出形式与 use 调用规则不匹配:对象需提供 install,函数会被直接调用。→ 修法:按一种规范导出:export default { install(Vue){...} } 或 export default function(Vue){...}。
  • 现象:vue.config.js 配了 host 却启动失败或访问异常。→ 原因:host 里误加协议,或端口/域名与 hosts 配置不一致。→ 修法:host 只写域名/IP,协议单独由 https 等配置控制;端口、hosts、访问地址三者对齐。

练习 ​

  1. 把入口文件改为 new Vue({ render: h => h(App) }).$mount('#app'),再分别测试“只写 template”和“同时写 render + template”的显示结果,记录优先级差异。
  2. 给 Child.vue 的 num 增加 type: Number、default: 100、required 三种校验组合,分别测试父组件传数字、传字符串、不传值时的控制台表现。
  3. 在 App.vue 和 Child.vue 中各写一个 h3 样式,先不加 scoped 复现冲突,再加 scoped 修复,并用开发者工具观察属性选择器变化。
  4. 把笔记中的局部过滤器改造成 src/filters/index.js,分别用“对象 + install”和“函数”两种插件写法通过 Vue.use() 注册,验证效果一致。
  5. 基于 todoList 页面完成:新增任务、全选/反选、删除单条、删除选中项;要求组件拆分清晰,父子通信用 props + $emit。

原视频对照 ​

#视频时长
1渲染函数render12:49
2脚手架项目创建34:57
3组件的使用18:02
4过滤器的基本使用15:28
5将过滤器作为插件进行安装09:39
6渲染todoList05:39

小结 ​

  • 单文件组件先保证结构层合法:template 不可省略,且只能有一个根元素。
  • 入口文件的核心是 render: h => h(App) 加手动挂载,render 与 template 不要混用。
  • 父子组件通信优先保持单向数据流:父传 props,子通过事件回传,不在子组件直接改 props。
  • 样式冲突优先用 scoped 解决;父组件不要随意命中子组件内部选择器。
  • 过滤器有作用域边界:局部过滤器只在当前组件可用,跨组件使用应注册为全局或做成插件。
  • Vue.use() 的插件导出必须符合约定:对象走 install,函数直接执行,二者不要混写。
  • 脚手架项目要避开中文、特殊字符、C 盘路径;host/port 配置要与 hosts 和访问地址一致。
  • 调试顺序固定为:先看编译错误,再看控制台警告,最后查样式作用域和数据流向。