国庆节10月1日
--


Vue 是"渐进式"的 JavaScript 框架,核心思想是数据驱动视图——引入 Vue 模块、创建应用实例并挂载到元素上,再用插值表达式把 data 里的数据渲染到页面
.webp)
18 篇最后那个”数据驱动版”员工列表,render() 是我们自己一个字符一个字符拼出来的:改数据 → 手动调 render() → 手动重新上色 → 手动重新绑事件。这一章要学的 Vue 干的就是这同一件事,但它把”拼字符串”和”重新渲染”全包了——我们只管改数据,页面自己跟着变。
Vue 的内容分三块:快速入门(这一篇)、常用指令(20 篇)、Ajax 异步请求(21 篇),最后用课程案例《员工列表(异步交互)》把三块串起来。
定义(PPT 原文):Vue 是一款用于构建用户界面的渐进式的 JavaScript 框架。(官方:https://cn.vuejs.org/)
这句话每个词都有信息量,拆开看:
| 词 | 意思 |
|---|---|
| 构建用户界面 | 它管的是”用户看到的那层页面”——把数据渲染成界面、把用户的输入收集回来 |
| 渐进式 | 能”由小到大”地用:只引一个 js 文件、改造页面上的一个局部也行;整套工程化方案做整站开发也行,不用一上手就上全套工具链 |
| JavaScript 框架 | 它本身是用 JS 写的、靠 JS 运行的;框架 = 一套完整的项目解决方案,用于快速构建项目 |
PPT 还专门讲了用框架的优点和缺点,很实在:
这句话翻译过来就是:框架帮你省掉大量重复劳动,代价是你要先学它那一套规矩。规矩在官网(中文文档质量很高),记不住就去查——和 16 篇说”DOM 的 API 不需要背、查文档或 AI”是同一个道理。
PPT 第 3 页用一张图说明:Vue 核心包只负责最基础的事,其它能力由配套的插件、工具补齐——
| 能力 | 由谁提供 | 说明 |
|---|---|---|
| 声明式渲染 | Vue 核心包 | 在 HTML 里”声明”数据和界面的关系(本篇章学的就是它) |
| 组件系统 | Vue 核心包 | 把页面拆成一个个可复用的部件 |
| 客户端路由 | VueRouter | 单页应用里”点链接换页面” |
| 状态管理 | Pinia / Vuex | 多个组件共用一份数据 |
| 构建工具 | Vite / Webpack | 打包、编译、热更新 |
两种使用方式:
| 方式 | 用到的部分 | 场景 |
|---|---|---|
| Vue 核心包开发 | 只引入一个 js 文件 | 局部模块改造(本篇章就属于这种:一个页面引一个 Vue 文件) |
| Vue 插件工程化开发 | 核心包 + 上面那一整套插件/工具 | 整站开发(后面的项目里会遇到) |
PPT 第 2 页用一个小例子把 Vue 的核心思想摆了出来——userList 是一份数据:
1// 一份数据(数组里放对象,就是 15 篇学过的写法)2userList: [3 {"id": 1, "name": "谢逊", "image": "1.jpg", "gender": 1, "job": 1},4 {"id": 2, "name": "韦一笑", "image": "2.jpg", "gender": 1, "job": 1}5]
userList 数据渲染出来的结果;数据和界面之间是”一套模板”的关系,不是一行行手写的 HTML这就是数据驱动视图(PPT 第 6 页标题下的那行字:基于数据渲染出用户看到的页面):
举个最小的对比——“把标题文字从 Hello 改成 Hi”:
1// 原生 DOM:要自己找到元素、自己改(16 篇的套路)2document.querySelector('#title').innerHTML = 'Hi';3
4// Vue:只需要改数据(页面上写着 {{title}},它会自己变)5this.title = 'Hi';这一章所有内容都建立在这一句上:数据是唯一的”真身”,页面只是它的展示。后面 v-for(列表)、v-if(显示隐藏)、v-model(表单)全都不是”操作页面元素”,而是在描述数据和界面的关系。
18 篇里我们手动实现了这套思想(render()),这一篇开始交给 Vue 自动做。
PPT 第 8 页是一张问答页,问的就是”Vue 的使用步骤”(这页的答案要背下来,写任何 Vue 页面都是这五步):
准备工作(三步)
createApp(...)数据驱动视图(两步)
data() 里{{ }}
<div id="app"> 里放 {{message}}),下半部分是 <script type="module"> 里的三件事:import 引入 Vue、createApp({ data() { return {...} } }) 准备数据、.mount('#app') 把实例挂到元素上Vue 是”官方提供的一个 js 文件”,引进来有两种方式:
1<!-- 方式一:在线引入(PPT 和课程代码用的方式)——直接写 CDN 地址 -->2<script type="module">3 // 从 unpkg(一个在线 CDN)加载 Vue 3 的"浏览器 ESM 版"4 import { createApp } from 'https://unpkg.com/vue@3/dist/vue.esm-browser.js';5</script>1<!-- 方式二:本地引入——把课程 js 目录里的 vue.esm-browser.js 拷到项目里 -->2<script type="module">3 // 路径按实际位置写(这里是"和 html 同级的 js 目录")4 import { createApp } from './js/vue.esm-browser.js';5</script>两种方式的差别不只是”在不在线”:
| 方式 | 好处 | 代价 |
|---|---|---|
| 在线(CDN 地址) | 不用下载文件;双击 html 就能直接跑(浏览器把 CDN 地址当成一次跨域请求,unpkg 允许跨域) | 必须联网;网速慢时首屏会等一下 |
| 本地(自己拷一份) | 断网也能开发;不怕 CDN 挂 | 必须用 Live Server 打开(见下面的警告框);文件 460KB 左右,要跟着项目走 |
用本地文件时,双击 html 打开会失败——不是代码写错,是浏览器拦的。ESM 模块的 import 受 CORS 限制,而 file:// 打开的页面来源是 null,Chrome 会直接拒绝加载本地模块文件,控制台里是这行红字:
1Access to script at 'file:///.../js/vue.esm-browser.js' from origin 'null' has been blocked by CORS policy这时的页面症状很好认:{{message}} 原样显示在页面上,Vue 完全没生效。
解决办法:用 04 篇装过的 Live Server——在 VS Code 里右键 html 文件 → Open with Live Server,地址栏会变成 http://127.0.0.1:5500/xxx.html(PPT 第 7 页截图的地址栏就是这个),这时 import 就正常了。
练习文件用的是在线 CDN 地址,所以双击即可运行(联网就行);想离线练就按上面这条路走。
这两个文件的来历:vue.esm-browser.js 是 Vue 的 ESM 浏览器构建版——它把 Vue 的运行时和模板编译器都打包进去了,所以能在浏览器里直接编译 {{ }} 这种模板,最适合”引一个 js 文件就能用”的场景。(工程化项目里会用 Vite 打包,编译器在构建阶段就把模板处理好了,那是后面的事。)
课程代码 12. Vue-快速入门.html 的完整写法(页面元素 + 脚本一共就这么点):
1<!-- 页面部分:准备一个元素,交给 Vue 控制(注意 id="app") -->2<div id="app">3 <h1>{{message}}</h1>4 <h1>{{count}}</h1>5</div>6
7<script type="module">8 // 1. 引入 Vue 模块(官方提供的 ESM 浏览器版)9 import { createApp } from 'https://unpkg.com/vue@3/dist/vue.esm-browser.js';10
11 // 2. 创建 Vue 应用实例12 createApp({13 // data 是一个函数,返回的是"数据模型"(页面要用到的数据都放这里)14 data() {15 return {16 message: 'Hello Vue',17 count: 10018 }19 }20 }).mount('#app'); // 3. 挂载:把实例交给 id="app" 的元素,之后它管理的区域就归 Vue 了21</script>逐句拆开:
| 代码 | 作用 |
|---|---|
import { createApp } from '...' | 从 Vue 模块里取出 createApp 这个函数(模块化导入的写法,17 篇提过 ESM 的 export / import) |
createApp({ ... }) | 创建一个 Vue 应用实例;括号里的对象叫”选项对象”,data、methods 等都写在这里面 |
data() { return { ... } } | 准备数据:必须写成函数,返回一个对象,对象里的属性就是页面上能用的变量 |
.mount('#app') | 挂载:括号里是 CSS 选择器,指向那个”交给 Vue 控制”的元素(写 #app 就是 id="app" 的元素) |
关于挂载点和脚本位置,有两个容易踩的点:
div#app。课程案例(员工列表)挂的就是 #container——.mount('#container')。写在 HTML 元素上的 id 是什么,mount 里就写什么。<script type="module"> 属于模块脚本,默认延迟执行(等价于加了 defer):浏览器先把 HTML 解析完,再执行它——所以即使写在 <head> 里,mount('#app') 也照样能找到元素。(课程代码放在 </body> 前面,是为了好读。)
对比一下:普通
<script>(13 篇的document.querySelector那种)没有这个待遇,放<head>里就会拿到null——这也是 Vue 的脚本通常不用操心位置的原因。
插值表达式就是两个大括号:把 data 里的数据显示到页面上。
1<div id="app">2 <h1>{{message}}</h1> <!-- 显示 data 里的 message:Hello Vue -->3 <h1>{{count}}</h1> <!-- 显示 data 里的 count:100 -->4 <h1>{{count + 1}}</h1> <!-- 也能写表达式:101 -->5</div>打开页面看到的效果:

127.0.0.1:5500/01.Vue快速入门.html(Live Server 起的本地服务),页面上 {{message}} 已经被替换成了 Hello Vue,说明 Vue 成功接管了 #app 这块区域插值表达式只能写在”标签内部的内容区”,不能写在标签的属性里。这是最容易踩的一个坑,先记住结论:
1<!-- 对的:放在两个标签之间 -->2<td>{{e.name}}</td>3
4<!-- 错的:写在属性值里,页面会原样显示这段花括号 -->5<img src="{{e.image}}">想动态设置属性要用 20 篇的 v-bind(简写 :src="e.image")。PPT 在员工列表代码旁边专门写了一句注释:“插值表达式是不能出现在标签内部”,说的就是这个。
页面原样显示 {{message}},怎么排查? 这是 Vue 新手最常遇到的问题,按顺序查四件事:
mount 的选择器和元素的 id 对得上吗——id="app" 配 .mount('#app');写成 #container 却挂在 #app 上,Vue 就管不到页面这块{{ }} 是不是写在挂载元素的外面——.mount('#app') 只管 #app 里面那一块,区域外的插值表达式没人管{{ }}——属性要用 v-bind(上面那条警告)| 问题 | 答案 |
|---|---|
| Vue 是什么? | 用于构建用户界面的渐进式 JavaScript 框架(官网 https://cn.vuejs.org/);框架 = 一套完整的项目解决方案,优点是提升开发效率,缺点是要记住它的使用规则 |
| “渐进式”体现在哪? | 只引一个 js 文件就能局部改造(本篇章的用法),配合 VueRouter / Pinia / Vite 等插件工具可以整站开发 |
| Vue 的核心思想? | 数据驱动视图——基于数据渲染出用户看到的页面;数据是”真身”,页面是它的展示结果 |
| Vue 的使用步骤? | 准备工作三步:引入 Vue 模块 → 创建应用实例 → 定义元素交给 Vue 控制;数据驱动视图两步:准备数据 → 插值表达式渲染 |
| 怎么引入 Vue? | 在线:import { createApp } from 'https://unpkg.com/vue@3/dist/vue.esm-browser.js';本地:把 vue.esm-browser.js 拷进项目后 import ... from './js/vue.esm-browser.js'(本地方式必须用 Live Server,双击打开会被 CORS 拦) |
| 怎么创建应用? | createApp({ data() { return { ... } } }).mount('#app')——data 是函数、返回对象;mount 里写挂载元素的选择器 |
| 插值表达式怎么写? | {{message}}(也能写表达式 {{count + 1}});只能写在标签内部,不能写在属性里;页面上原样显示花括号 = Vue 没生效 |
| 脚本放哪儿? | <script type="module"> 是模块脚本,默认延迟执行,放 <head> 也能拿到元素(普通 <script> 不行,要放 </body> 前) |
import { createApp } from 'https://unpkg.com/vue@3/dist/vue.esm-browser.js';本地把 vue.esm-browser.js 拷进项目后 import ... from './js/vue.esm-browser.js'<script type="module"> 里;本地文件方式不能双击打开(file:// 来源是 null,模块 import 被 CORS 拦截,控制台报 blocked by CORS policy),要用 Live Server 起 http://127.0.0.1:5500 打开;在线 CDN 地址双击就能跑createApp({ data() { return {...} } }).mount('#app')——data 必须是函数、返回一个对象;mount() 里写挂载元素的选择器(挂载点不一定是 #app,课程案例挂的是 #container){{message}},也能写表达式 {{count + 1}};只能写在标签内部,不能写在标签属性里(<img src="{{e.image}}"> 不生效,要用 v-bind)mount 选择器和元素 id 是否一致 → {{ }} 是否写在挂载元素内部 → 属性里是不是误写了 {{ }}<script type="module"> 属于模块脚本,默认延迟执行(等价 defer),写在 <head> 里也能拿到元素;普通 <script> 没有这个待遇,要放在 </body> 前面2-1 做出第一个 Vue 页面 页面上有一个大标题,要求它显示的文字来自”数据”,而不是写死在标签里。要求:
id="app")Hello Vuecount(值随意),标题里显示它的加 1 结果
(练习文件 test_19_第一个Vue页面.html 里已经准备好了标题和写作区,Vue 模块已经引入。)一级 · 思路:整个页面就三件事——把元素交给 Vue 管、把数据给 Vue、在页面上”占个位”显示数据
二级 · 方法:createApp({ ... }).mount('#app');数据放在 data() 里 return 出来的对象中;页面用插值表达式 {{ }} 取值;表达式可以直接算({{count + 1}})
三级 · 骨架:<h1>{{____}}</h1> / createApp({ data() { return { message: '____', count: ____ } } }).mount('____');
1<div id="app">2 <h1>{{message}}</h1>3 <h1>{{count + 1}}</h1>4</div>5
6<script type="module">7 import { createApp } from 'https://unpkg.com/vue@3/dist/vue.esm-browser.js';8
9 createApp({10 data() {11 return {12 message: 'Hello Vue',13 count: 10014 }15 }16 }).mount('#app');17</script>对照课程 12. Vue-快速入门.html。检查点:① 页面上是 Hello Vue 和 101,看不到花括号;② 把 message 改成别的字符串、刷新,标题跟着变(数据驱动视图)。
2-2 用数据结构渲染出多个位置的内容 页面上有一张”个人资料”小卡片:姓名、职位、城市三行文字,外加一行”简介”。要求:
test_19_插值表达式.html 里已经准备好了结构和写作区。)一级 · 思路:凡是”会变的内容”都放数据里;页面上只写一个”取值的位置”
二级 · 方法:数据都写在 data() 的 return { } 里;页面上用 {{ }};{{ }} 里可以写表达式(含字符串拼接 +)
三级 · 骨架:<p>{{____}}</p> / <p>来自{{____}}的开发者</p> / <span>{{visits ____ 1}}</span> / data() { return { name: '____', …… } }
1<div id="app">2 <p>访问次数:{{visits + 1}}</p>3 <h2>{{name}}</h2>4 <p>职位:{{position}}</p>5 <p>城市:{{city}}</p>6 <p>简介:来自{{city}}的{{position}}</p>7</div>8
9<script type="module">10 import { createApp } from 'https://unpkg.com/vue@3/dist/vue.esm-browser.js';11
12 createApp({13 data() {14 return {15 name: '令狐冲',16 position: '讲师',17 city: '杭州',18 visits: 9919 }20 }21 }).mount('#app');22</script>检查点:① 三行文字、简介、“访问次数 100”都对;② 把 visits 改成 0 刷新,页面显示 1(说明 {{ }} 里真的在算表达式,不是显示一段文字)。
2-3 诊断:为什么页面原样显示了花括号?
练习文件里是一个”坏掉的”Vue 页面:打开后页面上有两处原样显示着 {{title}},数据没渲染出来。要求:找出全部 3 处问题并改好——改完页面上只剩一个标题、内容是”传智教育”。
(直接在 test_19_排查错误.html 上改,3 处问题都在这一页里。)
1<body>2 <div id="app">3 <h1>{{title}}</h1>4 </div>5 <h1>{{title}}</h1>6
7 <script type="module">8 import { createApp } from 'https://unpkg.com/vue@3/dist/vue.esm-browser.js';9
10 createApp({11 data: {12 title: '传智教育'13 }14 }).mount('#container');15 </script>16</body>一级 · 思路:三处问题分别在”数据写法 / 挂载点 / 插值表达式的位置”上,都是这一篇讲过的规则;盯着 F12 控制台里的报错看,它会直说哪里不对
二级 · 方法:① data 必须是函数、返回对象(写成 data() { return {...} });② mount 里的选择器要和元素 id 一致(页面上是 #app,代码里写的是 #container);③ 挂载元素外面的那个 <h1> 归 Vue 管不着,得删掉
三级 · 骨架:data() { ____ { title: '传智教育' } } / .mount('____') / 删掉 </div> 后面那一行 <h1>……</h1>
1<body>2 <div id="app">3 <h1>{{title}}</h1>4 </div>5 <!-- 改法③:挂载区(#app)外面的插值表达式没人管,这一行要删掉 -->6
7 <script type="module">8 // 引入语句本来就是好的(CDN 地址),不用动9 import { createApp } from 'https://unpkg.com/vue@3/dist/vue.esm-browser.js';10
11 createApp({12 // 改法①:data 必须是函数,并且 return 一个对象13 data() {14 return {15 title: '传智教育'16 }17 }18 // 改法②:挂载点的选择器要写成元素真实存在的 id(原来是 #container,页面上是 #app)19 }).mount('#app');20 </script>21</body>三处问题的症状:① data 写成对象时控制台出现 Vue 的警告,页面上的数据取不到;② 挂载点写错时报”挂载目标选择器找不到”,整块 #app 纹丝不动、花括号原样露出;③ 挂载区外那一行永远不会被渲染——.mount('#app') 只管 #app 里面。改完记得再开一次控制台确认没有红字。
3-1 做一个”个人名片”页面(数据全部来自 data) 页面上有一张名片:顶部是大号姓名,下面一行”职位 · 城市”,再往下是一段简介、一行”技能标签”(三个标签),最下面是一个”访问次数”。要求:
data() 里,页面上只写取值的位置(不许写死文字)test_19_综合_个人名片.html 里已经准备好了基本骨架和写作区。)一级 · 思路:先把页面上”会变的内容”列一遍,全部搬进数据;页面上剩下的就是”哪里显示哪个变量”
二级 · 方法:数据写在 data() 的 return {} 里;显示用 {{ }};{{ }} 里能写 + 拼接;样式照常写在 <style> 里(06 篇)
三级 · 骨架:<h1>{{____}}</h1> / <p>{{position}} · {{____}}</p> / <p>{{intro}}</p> / <span class="tag">{{skill1}}</span> ... / <p>访问次数:{{visits + ____}}</p> / data() { return { name: '____', position: '____', city: '____', intro: '____', skill1: '____', skill2: '____', skill3: '____', visits: ____ } }
1<!DOCTYPE html>2<html lang="zh-CN">3<head>4 <meta charset="UTF-8">5 <title>个人名片</title>6 <style>7 #app {8 width: 360px; /* 卡片宽度 */9 margin: 40px auto; /* 水平居中 */10 padding: 20px;11 border: 1px solid #ddd;12 border-radius: 8px; /* 圆角 */13 font-family: "微软雅黑";14 }15 h1 { margin: 0; } /* 去掉默认的外边距 */16 .sub { color: #888; margin: 6px 0 16px; } /* 职位 · 城市 这一行用灰色 */17 .tag {18 display: inline-block;19 padding: 2px 8px;20 margin-right: 6px;21 background-color: #eef6ff; /* 标签浅蓝底 */22 border-radius: 4px;23 }24 .visits { color: #888; font-size: 12px; }25 </style>26</head>27<body>28 <div id="app">29 <h1>{{name}}</h1>30 <p class="sub">{{position}} · {{city}}</p>31 <p>{{intro}}</p>32 <p>33 <span class="tag">{{skill1}}</span>34 <span class="tag">{{skill2}}</span>35 <span class="tag">{{skill3}}</span>36 </p>37 <p class="visits">访问次数:{{visits + 1}}</p>38 </div>39
40 <script type="module">41 import { createApp } from 'https://unpkg.com/vue@3/dist/vue.esm-browser.js';42
43 createApp({44 data() {45 return {46 name: '令狐冲',47 position: '讲师',48 city: '杭州',49 intro: '一个把前端当爱好的后端学习者。',50 skill1: 'HTML',51 skill2: 'CSS',52 skill3: 'JavaScript',53 visits: 9954 }55 }56 }).mount('#app');57 </script>58</body>59</html>检查点:① 页面上看不到任何花括号;② 把数据里的姓名改成别的、刷新页面,名片上的姓名跟着变(这就是数据驱动视图);③ 访问次数显示 100;④ 卡片居中、标签有底色。
想一想:如果这个名片要显示 10 个技能标签,写 10 个 <span> 是不是很笨?20 篇的 v-for 就是为这件事准备的。
如果你喜欢,那么欢迎来到我的世界!
了解更多暂未播放



"所有的相遇都是久别重逢。"—— 未知


