Vue2学习笔记(二)
一、自定义指令
需求1:定义一个v-big指令,和v-text功能类似,但会把绑定的数值放大10倍。
需求2:定义一个v-fbind指令,和v-bind功能类似,但可以让其所绑定的input元素默认获取焦点。
1.定义语法:
(1).局部指令:
javascript
new Vue({ new Vue({directives:{指令名:配置对象} 或 directives{指令名:回调函数}}) })
(2).全局指令:
Vue.directive(指令名,配置对象) 或 Vue.directive(指令名,回调函数)
2.配置对象中常用的3个回调:
(1).bind:指令与元素成功绑定时调用。
(2).inserted:指令所在元素被插入页面时调用。
(3).update:指令所在模板结构被重新解析时调用。
new Vue({el: '#root',data: {name: '尚硅谷',n: 1},directives: {big(element, binding) {console.log('big', this) //注意此处的this是window// console.log('big')element.innerText = binding.value * 10},fbind: {//指令与元素成功绑定时(一上来)bind(element, binding) {element.value = binding.value},//指令所在元素被插入页面时inserted(element, binding) {element.focus()},//指令所在的模板被重新解析时update(element, binding) {element.value = binding.value}}}})
3.备注:
- 指令定义时不加v-,但使用时要加v-;
<h2>放大10倍后的n值是:<span v-big="n"></span> </h2>big(element, binding) {console.log('big', this) //注意此处的this是window// console.log('big')element.innerText = binding.value * 10},
- 指令名如果是多个单词,要使用kebab-case命名方式,不要用camelCase命名。
<h2>放大10倍后的n值是:<span v-big-number="n"></span> </h2>'big-number'(element,binding){// console.log('big')element.innerText = binding.value * 10},
- 上面的big函数在何时会被调用?
- 1.指令与元素成功绑定时(一上来)。
- 2.指令所在的模板被重新解析时。
回顾一个DOM操作:
<!DOCTYPE html>
<html><head><meta charset="UTF-8" /><title>Document</title><style>.demo {background-color: orange;}</style>
</head><body><button id="btn">点我创建一个输入框</button><script type="text/javascript">const btn = document.getElementById('btn')btn.onclick = () => {const input = document.createElement('input')input.className = 'demo'input.value = 99input.onclick = () => { alert(1) }document.body.appendChild(input)input.focus()input.parentElement.style.backgroundColor = 'skyblue'console.log(input.parentElement)}</script>
</body></html>

chatgpt总结:
Vue2 自定义指令知识总结如下:
一、定义
自定义指令(Custom Directive)用于对DOM元素进行底层操作。
常用于封装一些与DOM直接交互的逻辑,如聚焦、拖拽、懒加载等。
二、注册方式
1. 全局注册
Vue.directive('指令名', {// 钩子函数
})
示例:
Vue.directive('focus', {inserted(el) {el.focus()}
})
使用:
<input v-focus>
2. 局部注册
在组件中定义:
directives: {focus: {inserted(el) {el.focus()}}
}
三、指令钩子函数
Vue2 中指令对象包含以下钩子函数:
| 钩子 | 触发时机 | 作用 |
|---|---|---|
bind(el, binding, vnode) | 指令第一次绑定到元素时 | 初始化操作 |
inserted(el) | 元素插入父节点时 | 可操作DOM |
update(el, binding) | 所在组件更新时(可能多次调用) | 响应数据变化 |
componentUpdated(el, binding) | 所在组件及子组件更新后 | 确保DOM最新 |
unbind(el) | 指令解绑时 | 清理资源 |
钩子示例
Vue.directive('color', {bind(el, binding) {el.style.color = binding.value},update(el, binding) {el.style.color = binding.value}
})
使用:
<p v-color="'red'">文字变红</p>
四、binding 对象属性
指令的第二个参数 binding 常用字段:
| 属性 | 含义 | 示例 |
|---|---|---|
value | 传入的值 | v-color="'red'" → 'red' |
oldValue | 更新前的值 | 用于对比变化 |
arg | 指令参数 | v-color:background="'blue'" → 'background' |
modifiers | 修饰符对象 | v-color.bold → { bold: true } |
name | 指令名称 | 'color' |
binding 示例
Vue.directive('style', {bind(el, binding) {console.log(binding)}
})
使用:
<p v-style:font.bold="'red'">测试</p>
输出:
{name: "style",value: "red",arg: "font",modifiers: { bold: true }
}
五、实用示例
1. 自动聚焦
Vue.directive('focus', {inserted(el) {el.focus()}
})
2. 权限控制
Vue.directive('permission', {inserted(el, binding) {if (!binding.value) el.parentNode.removeChild(el)}
})
3. 图片懒加载
Vue.directive('lazy', {inserted(el, binding) {const observer = new IntersectionObserver(([entry]) => {if (entry.isIntersecting) {el.src = binding.valueobserver.unobserve(el)}})observer.observe(el)}
})
六、注意事项
- 自定义指令只能用于普通DOM元素,不用于组件上。
- 不推荐频繁操作DOM(破坏Vue数据驱动原则)。
- 若逻辑复杂,考虑使用组件封装。
二、生命周期
Vue 实例从创建到销毁,会经历一系列 初始化、渲染、更新、销毁 的过程。
这些不同阶段称为 生命周期(Lifecycle),在每个阶段 Vue 都提供了 生命周期钩子函数(Hooks),供你在特定时机执行逻辑。
1.生命周期基本知识:
- 1.又名:生命周期回调函数、生命周期函数、生命周期钩子。
- 2.生命周期钩子是什么?:指Vue在关键时刻帮我们调用的一些特殊名称的函数。
- 3.生命周期函数的名字不可更改,但函数的具体内容是程序员根据需求编写的。
- 4.生命周期函数中的this指向是vm 或 组件实例对象。
2.生命周期的八个钩子
new Vue({el:'#root',template:`<div><h2>当前的n值是:{{n}}</h2><button @click="add">点我n+1</button></div>`,data:{n:1},methods: {add(){console.log('add')this.n++},bye(){console.log('bye')this.$destroy()}},watch:{n(){console.log('n变了')}},beforeCreate() {console.log('beforeCreate')},created() {console.log('created')},beforeMount() {console.log('beforeMount')},mounted() {console.log('mounted')},beforeUpdate() {console.log('beforeUpdate')},updated() {console.log('updated')},beforeDestroy() {console.log('beforeDestroy')},destroyed() {console.log('destroyed')},})
3.总结生命周期
常用的生命周期钩子:
1.mounted: 发送ajax请求、启动定时器、绑定自定义事件、订阅消息等【初始化操作】。
2.beforeDestroy: 清除定时器、解绑自定义事件、取消订阅消息等【收尾工作】。关于销毁Vue实例
1.销毁后借助Vue开发者工具看不到任何信息。
2.销毁后自定义事件会失效,但原生DOM事件依然有效。
3.一般不会在beforeDestroy操作数据,因为即便操作数据,也不会再触发更新流程了。
4.生命周期的原理图

chatgpt总结
1.生命周期各个时期的详解
| 阶段 | 钩子函数 | 触发时机 | 能做的事 | 注意点 |
|---|---|---|---|---|
| 创建阶段 | beforeCreate | 实例初始化之后,数据观测(data/props)和事件尚未配置 | 不能访问 data、computed、methods、props | 一般不使用 |
created | 实例创建完成,数据和方法已可用,但尚未挂载 DOM | 可进行数据请求、初始化数据 | 无法访问 $el(DOM 尚未生成) | |
| 挂载阶段 | beforeMount | 模板已编译,但尚未渲染到页面 | 可在挂载前最后修改数据 | 通常不常用 |
mounted | 组件挂载完成,DOM 已生成,可访问 $el | 常用于 DOM 操作、网络请求、第三方库初始化 | 若有子组件,需等待 $nextTick() 后才能保证全部挂载完成 | |
| 更新阶段 | beforeUpdate | 响应式数据更新,DOM 尚未重新渲染 | 可在数据变化前获取旧 DOM 状态 | 不可在此修改数据,否则陷入死循环 |
updated | 数据变更导致 DOM 重新渲染后触发 | 可访问更新后的 DOM | 避免在此修改数据 | |
| 销毁阶段 | beforeDestroy | 实例销毁前触发 | 清理定时器、解绑事件、取消订阅等 | 实例仍然可用 |
destroyed | 实例销毁后触发 | 所有指令解绑,事件监听移除 | 无法再访问数据和 DOM |
2.父子组件生命周期的执行顺序
在 父组件包含子组件 的情况下:
父 beforeCreate →
父 created →
父 beforeMount →
子 beforeCreate →
子 created →
子 beforeMount →
子 mounted →
父 mounted
销毁时顺序相反:
父 beforeDestroy →
子 beforeDestroy →
子 destroyed →
父 destroyed
三、组件
定义:实现应用中局部功能代码和资源的集合
1.对组件的理解
传统的方式进行编写代码实现应用的设计存在的问题:
- 依赖关系混乱,不好维护
- 代码复用率不高

知识点总结:
(1)模块:
1.理解:向外进行提供特定功能的js程序,一般是js文件
2.为什么使用:js文件较多并且复杂
3.作用:可以实现js的复用,提高js的运行效率
(2)组件:
1.理解:用来实现局部(特定)功能效果的代码集合(html、css、js、image……)
2.为什么:一个界面的功能十分复杂
3.作用:实现复用编码的功能,简化项目编码的流程,提高运行效率
(3)模块化:
当应用中的js都以模块来编写的,那这个应用的就是一个模块化的应用。
(4)组件化:
当应用中的功能都是用多组件的方式进行编写的,那么这个应用就是一个组件化的应用。
2.非单文件组件
一个文件中可以有很多个组件
1.Vue中使用组件的三大步骤:
一、定义组件(创建组件)
二、注册组件
三、使用组件(写组件标签)
2.具体说明:
一、如何定义一个组件?
使用Vue.extend(options)创建,其中options和new Vue(options)时传入的那个options几乎一样,但也有点区别;
区别如下:
1.el不要写,为什么? ——— 最终所有的组件都要经过一个vm的管理,由vm中的el决定服务哪个容器。
2.data必须写成函数,为什么? ———— 避免组件被复用时,数据存在引用关系。
备注:使用template可以配置组件结构。
const school = Vue.extend({template:`<div class="demo"><h2>学校名称:{{schoolName}}</h2><h2>学校地址:{{address}}</h2><button @click="showName">点我提示学校名</button> </div>`,// el:'#root', //组件定义时,一定不要写el配置项,因为最终所有的组件都要被一个vm管理,由vm决定服务于哪个容器。data(){return {schoolName:'尚硅谷',address:'北京昌平'}},methods: {showName(){alert(this.schoolName)}},})//第一步:创建student组件const student = Vue.extend({template:`<div><h2>学生姓名:{{studentName}}</h2><h2>学生年龄:{{age}}</h2></div>`,data(){return {studentName:'张三',age:18}}})//第一步:创建hello组件const hello = Vue.extend({template:`<div> <h2>你好啊!{{name}}</h2></div>`,data(){return {name:'Tom'}}})
二、如何注册组件?
1.局部注册:靠new Vue的时候传入components选项
2.全局注册:靠Vue.component('组件名',组件)
const school = Vue.extend({template:`<div class="demo"><h2>学校名称:{{schoolName}}</h2><h2>学校地址:{{address}}</h2><button @click="showName">点我提示学校名</button> </div>`,// el:'#root', //组件定义时,一定不要写el配置项,因为最终所有的组件都要被一个vm管理,由vm决定服务于哪个容器。data(){return {schoolName:'尚硅谷',address:'北京昌平'}},methods: {showName(){alert(this.schoolName)}},})//第一步:创建student组件const student = Vue.extend({template:`<div><h2>学生姓名:{{studentName}}</h2><h2>学生年龄:{{age}}</h2></div>`,data(){return {studentName:'张三',age:18}}})//第一步:创建hello组件const hello = Vue.extend({template:`<div> <h2>你好啊!{{name}}</h2></div>`,data(){return {name:'Tom'}}})
三、编写组件标签:
<school></school>
在这里插入代码片
<!-- 第三步:编写组件标签 --><school></school><hr><!-- 第三步:编写组件标签 --><student></student><hr><student></student>
3.注意点
1.关于组件名:
一个单词组成:
第一种写法(首字母小写):school
第二种写法(首字母大写):School多个单词组成:
第一种写法(kebab-case命名):my-school,使用需要用''括起来
第二种写法(CamelCase命名):MySchool (需要Vue脚手架支持)
备注:
(1).组件名尽可能回避HTML中已有的元素名称,例如:h2、H2都不行。
(2).可以使用name配置项指定组件在开发者工具中呈现的名字。
2.关于组件标签:
第一种写法:<school></school>
第二种写法:<school/>
备注:不用使用脚手架时,<school/>会导致后续组件不能渲染。
3.一个简写方式:
const school = Vue.extend(options) 可简写为:const school = options
4.总结以及对比
可以。下面是 Vue 2 中组件(Component)与非单文件组件(Non-SFC) 的系统总结,清晰区分两者的概念、使用方式和应用场景。
🧩 一、什么是组件(Component)
组件是 Vue 的核心思想之一。
它允许你把页面拆分为多个可复用的、独立的模块。每个模块拥有自己的模板(HTML)、逻辑(JavaScript)和样式(CSS)。
组件是可复用的 Vue 实例,拥有自己的数据、模板、生命周期和方法。
🧱 二、Vue 2 组件的分类
| 类型 | 定义方式 | 特点 |
|---|---|---|
| 单文件组件(SFC) | .vue 文件(包含 <template>, <script>, <style>) | 模块化强、结构清晰,现代 Vue 项目主流用法 |
| 非单文件组件(传统组件) | 直接用 Vue.component() 或 components 注册对象定义 | 常用于简单场景、在线编辑、学习或演示 |
⚙️ 三、非单文件组件(Non-SFC)
1️⃣ 定义方式(全局注册)
在 main.js 或 HTML script 中 使用:
Vue.component('my-component', {template: '<div>{{ message }}</div>',data() {return {message: "我是非单文件组件"};}
});
使用:
<div id="app"><my-component></my-component>
</div><script>
new Vue({el: '#app'
});
</script>
2️⃣ 局部注册
在某个组件(或根实例)中使用:
var Child = {template: '<p>{{ text }}</p>',data() {return { text: '局部组件内容' };}
};new Vue({el: '#app',components: {'child-component': Child}
});
3️⃣ 特点
- 直接写在 JavaScript 中;
- 模板是字符串(写在
template属性里); - 没有文件分离,结构不清晰;
- 无法使用构建工具编译(例如 ES 模块、预处理器);
- 适合简单、小规模项目或原型。
📦 四、单文件组件(SFC)
单文件组件(Single File Component)以 .vue 为后缀,是 Vue 工程化开发的标准组件格式。
1️⃣ 基本结构
<template><div class="hello">{{ msg }}</div>
</template><script>
export default {name: 'HelloWorld',data() {return { msg: 'Hello Vue!' };}
}
</script><style scoped>
.hello { color: blue; }
</style>
2️⃣ 特点
| 优点 | 说明 |
|---|---|
| 结构清晰 | 模板、逻辑、样式分离 |
| 可编译 | 支持构建工具(Webpack、Vite) |
| 可模块化 | 支持 import / export |
| 可复用 | 组件间独立、可传参(props)、可通信(emit) |
| 样式隔离 | scoped 属性避免样式污染 |
🔄 五、组件通信方式总结
| 场景 | 方式 | 示例 |
|---|---|---|
| 父 → 子 | props | <child :msg="dataFromParent"/> |
| 子 → 父 | $emit | this.$emit('eventName', data) |
| 兄弟组件 | 事件总线 / Vuex | bus.$emit 与 bus.$on |
| 多层嵌套 | provide / inject | 用于深层传递数据 |
| 全局共享 | Vuex(状态管理) | 统一存储、管理全局状态 |
🧠 六、二者的主要区别总结
| 对比维度 | 非单文件组件 | 单文件组件 |
|---|---|---|
| 定义方式 | JS 中用 Vue.component() | .vue 文件定义 |
| 模板写法 | 写在字符串内 | <template> 块 |
| 样式支持 | 无法作用域隔离 | 可使用 <style scoped> |
| 工程支持 | 无法使用构建工具 | 完全支持 Webpack/Vite |
| 复用性 | 低 | 高 |
| 适用场景 | 学习、简单页面、CDN 引入 | 工程化开发、组件库 |
🧩 七、总结一句话
非单文件组件适合“手写、快速演示、学习原理”;
单文件组件适合“工程化开发、多人协作、模块化维护”。
5.组件的嵌套
设置一个app作为管理所有组件的组件
<body><div id="root"><app></app><hr></div><script type="text/javascript">// 学生组件const student=Vue.extend({template:`<div><h3>学生姓名:{{studentName}}</h3><h3>学生年龄:{{studentAge}}</h3></div>`,data(){return{studentName:'tom',studentAge:18,}},})// 学校组件const school=Vue.extend({template:`<div><h3>学校名称:{{schoolName}}</h3><h3>学校地址:{{schoolAddress}}</h3><student></student></div>`,// 注册给谁就在谁里面写data(){return{schoolName:'尚硅谷',schoolAddress:'北京',}},// 局部注册components:{student// 还没生成出来就调用了student,所以得把student写在前面}})const app={template:`<div><school></school></div>`,components:{// 让app做一人之下(vm)万人之上的管理者school,}}// 创建Vueconst vm=new Vue({el:'#root',components:{app,}})</script>
</body>

6.VueComponent构造函数
一、定义:
(1)school组件本质上是一个名为VueComponent的构造函数,且不是程序员定义的,是Vue.extend生成。
(2)我们只需要写或,Vue解析时会帮我们创建school组件的实例对象,即Vue帮我们执行的:new VueComponent(options)。
(3)特别注意:每次调用Vue.extend,返回的都是一个全新的VueComponent构造函数(一个组件一个Vc),而不是返回VueComponent的实例,只有当Vue解析组件时,才会返回所对应组件(如school)的VueComponent的实例对象
(4)VueComponent中的this指向
* 组件配置中
data函数、methods中的函数、watch中的函数、computed中的函数。它们的this均是VueComponent实例对象。* new Vue(options)配置中
data函数、methods中的函数、watch中的函数、computed中的函数 它们的this均是Vue实例对象。
(5)VueComponent的实例对象,以后简称vc(也可称之为:组件实例对象)。Vue的实例对象,以后简称vm。
7.一个重要的内置关系
(1)一个重要的内置关系:VueComponent.prototype.__proto__ === Vue.prototype
(2)为什么要有这个关系:让组件实例对象(vc)可以访问到 Vue原型上的属性、方法。

3.单文件组件
一个文件只能有一个组件(.Vue)常用
.Vue里面就是组件,是绝对不能写new Vue
1.有中转变量时的export的三种方法:
- 分别暴露:export const school=Vue.extend({})
- 统一暴露:export{school}
- 默认暴露:export default school
注意:但是暴露的东西是一个的时候用默认暴露比较多,因为引入的时候就是importXXfromXX 统一的还得import{XXX}
fromXXX
2.没中转变量的时候:
写了还得引入所以干脆就不用中转变量了:export default Vue.extend({
再简写(还得加name,与vue文件名保持一致):
export default {name:'School',data(){return{schoolName:'尚硅谷',schoolAddress:'北京',}}
}
3.单文件组件

- index.html
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>练习单文件组件语法</title>
</head>
<body><div id="root"><App></App></div><script type="text/javascript" src="main.js"></script><script type="text/javascript" src="../vue.js"></script><!-- 这个要是写在最上面了root盒子还没出来显示不了 -->
</body>
</html>
- main.js
import App from './App.vue'
new Vue({el:'#root',components:{App}
})
- app.vue
<template><!-- 组件结构 --><div><School></School><Student></Student></div>
</template><script>
// 引入importimport Student from './Student.vue'
import School from './School.vue'
// 交互的样式
export default {name:'App',components:{Student,School}
}</script>
- School.vue
<template><!-- 组件结构 --><div class="demo"><h3>学校名称:{{name}}</h3><h3>学校地址:{{address}}</h3></div>
</template><script>
// 交互的样式
export default {name:'School',data(){return{name:'尚硅谷',address:'北京',}}
}</script><style>/* 组件样式 */.demo{background-color: rgb(182, 65, 84);}
</style>
- Student.vue
<template><!-- 组件结构 --><div class="demo1"><h3>学生姓名:{{name}}</h3><h3>学生年龄:{{age}}</h3></div>
</template><script>
// 交互的样式
export default {name:'Student',data(){return{name:'tom',// 都用name也没事,这是两个文件age:18,}}
}</script><style>/* 组件样式 */.demo1{background-color: pink;}
</style>
