VUE2相关
我的博客可能需要缩款宽屏幕观看,图片无法均衡,很抱歉。
1. 指令、插值
2. computed 和 watch
computed 计算属性,v-model要有get 和 set
<template>
<div>
<p>num :{
{
num }} 答案: 20</p>
<p>double1 {
{
double1 }} 答案: 40</p>
<input v-model="double2" />
</div>
</template>
<script>
export default {
data() {
return {
num: 20,
}
},
computed: {
double1() {
return this.num * 2
},
double2: {
// v-model使用一定要有 get和set
get() {
// 如果只有一个 get 或 set 会报错
return this.num * 2
},
set(val) {
this.num = val / 2
},
},
},
}
</script>
watch监听,引用类型, 拿不到 oldVal。
<template>
<div>
<input v-model="name" />
<input v-model="info.city" />
</div>
</template>
<script>
export default {
data() {
return {
name: 'watch监听',
info: {
city: '北京',
},
}
},
watch: {
// watch监听
name(oldVal, val) {
// 值类型,可正常拿到 oldVal 和 val
console.log('watch name', oldVal, val)
},
info: {
handler(oldVal, val) {
// 引用类型, 拿不到 oldVal 。 因为指针相同,此时已经指向了新的 val
console.log('watch info', oldVal, val)
},
deep: true, // 深度监听
},
},
}
</script>
3. class 和 style
4. 条件渲染
5. 循环列表 v-for
6. 事件 event
- 1. event 事件是原生的。 2. 事件被挂载到当前元素。
7. 事件修饰符
8. 表单
<template>
<div>
<p>输入框 : {
{
name }}</p>
<!-- 1. trime 截取前后空格
2. lazy 类似防抖效果
3. number 转换为数字 -->
<input type="text" v-model.trim="name" />
<input type="text" v-model.lazy="name" />
<input type="text" v-model.number="age" />
<p>多行文本 : {
{
desc }}</p>
<textarea v-model="desc"></textarea>
<!-- 注意,<textarea> {
{
desc }} </textarea> 是不允许的!!!-->
<p>复选框 {
{
checked }}</p>
<input type="checkbox" v-model="checkbox" />
<p>多个复选框 {
{
checkedNames }}</p>
<input type="checkbox" id="jack" value="Jack" v-model="checkedNames" />
<label for="jack">Jack</label>
<input type="checkbox" id="join" value="John" v-model="checkedNames" />
<label for="john">John</label>
<input type="checkbox" id="mike" value="Mike" v-model="checkedNames" />
<label for="mike">Mike</label>
<p>单选{
{
gender }}</p>
<input type="radio" id="male" value="male" v-model="gender" />
<label for="male">男</label>
<input type="radio" id="female" value="female" v-model="gender" />
<label for="female">女</label>
<p>下拉列表选择 {
{
selected }}</p>
<select v-model="selected">
<option disabled value="">请选择</option>
<option>A</option>
<option>B</option>
<option>C</option>
</select>
<p>下拉列表选择 (多选) {
{
selectedList }}</p>
<select v-model="selectedList" multiple>
<option disabled value="">请选择</option>
<option>A</option>
<option>B</option>
<option>C</option>
</select>
</div>
</template>
<script>
export default {
data() {
return {
name: '德玛西亚',
age: 18,
desc: '自我介绍',
checked: true, // 单个复选框
checkedNames: [], // 多个复选框
gender: 'male', // 性别
selected: '', // 下拉列表单选
selectedList: [], // 下拉列表多选
}
},
}
</script>
9. VUE组件使用
10. 自定义 v-model
11. $nextTick
<template>
<div id="app">
<ul ref="ul1">
<li v-for="(item, index) in list" :key="index">
{
{
item }}
</li>
</ul>
<button @click="addItem">添加一项</button>
</div>
</template>
<script>
export default {
name: 'app',
data() {
return {
list: ['a', 'b', 'c'],
}
},
methods: {
addItem() {
this.list.push(`${
Date.now()}`)
this.list.push(`${
Date.now()}`)
this.list.push(`${
Date.now()}`)
// 获取 DOM 元素
const ulElem = this.$refs.ul1
console.log(ulElem.childNodes.length)
},
},
}
</script>
12. slot多种用法
- 1. 默认插槽
- 2.具名插槽
如果我们需要在同一个组件使用多个插槽,那么就需要使用具名插槽
- 3. 作用域插槽
- 作用域插槽
13. 动态组件
<template>
<div>
<!-- 动态组件 -->
<component :is="NextTickName"></component>
<!-- 动态组件 -->
<div v-for="(val, key) in newsData" :key="key">
<component :is="val.type"></component>
</div>
</div>
</template>
<script>
import NextTick from './NextTick'
export default {
components: {
// 注册组件
NextTick,
},
data() {
return {
// 动态组件
NextTickName: 'NextTick',
// 动态组件
newsData: {
1: {
type: 'text',
},
2: {
type: 'text',
},
3: {
type: 'image',
},
},
}
},
}
</script>
14. 异步组件
15. Keep-alive 缓存组件
<template>
<div>
<button @click="changeState('A')">A</button>
<button @click="changeState('B')">B</button>
<button @click="changeState('C')">C</button>
<KeepAliveStageA v-if="state === 'A'" />
<KeepAliveStageB v-if="state === 'B'" />
<KeepAliveStageC v-if="state === 'C'" />
</div>
</template>
<script>
import KeepAliveStageA from './KeepAliveStateA' // 引入组件
import KeepAliveStageB from './KeepAliveStateB'
import KeepAliveStageC from './KeepAliveStateC'
export default {
components: {
KeepAliveStageA,
KeepAliveStageB,
KeepAliveStageC, // 注册组件
},
data() {
return {
state: 'A', // 默认显示 A 组件
}
},
methods: {
// 点击事件,动态传值,修改 data中的state,从而改变组件
changeState(state) {
this.state = state
},
},
}
</script>
16. mixin 的使用
17. VUEX
18. vue-router
高阶,做好准备,Let’s see
1. vue原理
2. 如何理解MVVM
3. Vue响应式
// 触发更新视图
function updateView() {
console.log('视图更新');
}
// 监听数组,需要重新定义数组的原型
const oldArrayProperty = Array.prototype
// 创建新对象,原型指向 oldArrayProperty, 再扩展新的方法不会影响原型
const arrProto = Object.create(oldArrayProperty)
// methodName 其实就是 push,pop等 这些名字
['push', 'pop', 'shift', 'unshift', 'splice'].forEach(methodName => {
// 对每个方法都进行一个 定义
arrProto[methodName] = function () {
updateView() // 触发视图更新
oldArrayProperty[methodName].call(this, ...arguments)
// oldArrayProperty 方法就是 Array.prototype.call(this,...arguments)
}
})
// 重新定义属性,监听起来
// (target 就是data , key 就是data中的name或age , value 就是name的值zhangsan 或 age 的值20)
function defineReactive(target, key, value) {
// 深度监听,针对 info 层级比较深的 结构
observer(value)
// 核心 API
Object.defineProperty(target, key, {
get() {
// 获取
return value
},
set(newValue) {
// 设置
// 例如 data.name = '测试名字' 就会走这里
if (newValue !== value) {
// 针对 info 也需要深度监听
observer(newValue)
// 设置新值
// 注意,value 一直在闭包中,此处设置完之后,再get时也是会获取最新的数据
value = newValue
//触发更新视图
updateView()
}
}
})
}
// 监听对象属性
function observer(target) {
if (typeof target !== 'object' || target === null) {
//监听不是对象或数组,返回
return target
}
// 污染全局 Array 原型 (一定不可以!!!)
// 必须重新定义原型 不可污染全局 Array 原型
// Array.prototype.push = function () {
// updateView()
// ...
// }
// 如果是数组
if (Array.isArray(target)) {
target._proto_ = arrProto
}
// 重新定义各个属性 (for in 也可以遍历数组)
for (let key in target) {
defineReactive(target, key, target[key])
}
}
// 准备数据
const data = {
name: 'zhangsan',
age: 20,
// 针对 info 来说target 就是data , key 就是info , age 就是对象{}
info: {
address: '北京' // 需要深度监听
},
nums: [10, 20, 30]
}
// 监听数据
observer(data)
// 测试
data.name = '测试名字'
data.age = 25
// console.log('age', data.age); // 打印结果'age', 25
// data.x = '100' // 新增属性,监听不到 -- 所以有 Vue..set
// delete data.name // 删除属性,监听不到 -- 所以有 Vue.delete
data.info.address = '上海' // 深度监听
data.nums.push(4) // 监听数组
- 拿代码中 info 举例,如果
info: { { {}}}
像这样层级较深,它会一直走observer(value),一直递归到底
4. 虚拟 DOM (Virtual DOM)和 diff算法
5. 模版编译
// 依赖 vue-template-compiler 转换模版
const compiler = require('vue-template-compiler')
// 插值
const template = `<p>{
{message}</p>`
with (this) {
return _c('p', [_v(_s(message))]) }
// with (this) { return createElement('p', [createTextNode(toString(message))]) }
// h 函数 执行返回的 是 vnode
// createElement 执行返回的也是 vnode
// 表达式
const template1 = `<p>{
{ flag? message: 'no message found' }}</p>`
with (this) {
return _c('p', [_v(_s(flag ? message : 'no message found'))]) }
with (this) {
return createElement('p', [createTextNode(toString(flag ? message : 'no message found'))]) }
// 属性 和 动态属性
const template2 = `
<div id='div' class="container">
<img :src = "imgUrl"/>
</div> `
// staticClass 原先是 className ,再 vue 编译完后变成了staticClass
with (this) {
return _c('div'), {
staticClass: "container", attrs: {
"id": "div1" } }, [_c('img')] }
with (this) {
return createElement('div'), {
staticClass: "container", Attributes: {
"id": "div1" } }, [createElement('img')] }
// 条件编译
const template3 = `
<div>
<p v-if="flag ==='a'">A</p>
<p v-else>B</p>
</div>
`
with (this) {
return _c('div', ['div', [(flag === 'a') ? _c('p', [_v("A")]) : _c('p', [_v("B")])]]) }
with (this) {
return createElement('div', ['div', [(flag === 'a') ? createElement('p', [createTextNode("A")]) : createElement('p', [createTextNode("B")])]]) }
// 循环
const template4 = `
<ul>
<li v-for="item in list" :key="item.id">{
{item.title}}</li>
</ul> `
with (this) {
return _c('ul', _l((list), function (item) {
return _c('li', {
key: item.id }, (_v(_s(item.title)))) })) }
with (this) {
return createElement('ul', renderList((list), function (item) {
return createElement('li', {
key: item.id }, (createTextNode(toString(item.title)))) })) }
// 事件
const template5 = `<button @click = "clickHandler">submit</button>` // on代表所有的事件
with (this) {
return _c('button', {
on: {
"click": clickHandler } }, [_v("submit")]) }
with (this) {
return createElement('button', {
on: {
"click": clickHandler } }, [createTextNode("submit")]) }
// v-model
const template6 = `<input type="text" v-model="name">`
with (this) {
return _c('input', {
directives: [{
name: "model", rawName: "v-model", value: (name), expression: "name" }],
attrs: {
"type": "text" }, domProps: {
"value": (name) },
on: {
"input": function ($event) {
if ($event.target.composing) return; name = $event.target.value } }
})
}
// 上面都是 render 函数
// 执行 render 函数 返回 vnode
// 编译
const res = compiler.compile(template)
6. 组件 渲染/更新 过程
7. 异步渲染,参考上面 $nextick
8. 路由vue-router原理
hash 路由 (#)
<body>
<p>hash test</p>
<button id="btn1">修改 hash</button>
<script>
// hash变化 :包括
// a: JS 修改 url
// b: 手动修改 url 的 hash , 不会触发刷新,不会触发前进后退
// C: 浏览器前进、后退 , 不会触发刷新
window.onhashchange = (event) => {
console.log('old url', event.oldUrl);
console.log('new url', event.newUrl);
console.log('hash:', location.hash);
}
// 页面初次加载, 获取 hash
document.addEventListener('DOMContentLoaded', () => {
console.log('hash:', location.hash);
})
// JS 修改 url
document.getElementById('btn1').addEventListener('click', () => {
location.href = '#/user'
})
</script>
</body>
- window.onhashchange 是 JavaScript 中的一个事件处理程序。
- 用于检测 URL 中的哈希值(即 # 号后面的内容)是否发生了变化。
- 当浏览器的历史记录中前进或后退时,此事件也会被触发。
- 该事件常用于构建单页面应用程序(SPA),以便在 URL 哈希值更改时更新页面内容而无需重新加载整个页面。
history 路由
<body>
<p>history API test</p>
<button id="btn1">修改 hash</button>
<script>
// 页面初次加载,获取 path
document.addEventListener('DOMContentLoaded', () => {
console.log('load', location.pathname); // index.html
})
// 【注意】 用 pushState 方式, 浏览不会刷新页面
document.getElementById('btn1').addEventListener('click', () => {
const state = {
name: 'page1' }
console.log('切换路由到', 'page1');
history.pushState(state, '', 'page1') //重要!!
})
// 监听 浏览器前进、后退
window.onpopstate = (event) => {
// 重要!!
console.log('onpopstate', event.state, location.pathname);
}
</script>
</body>
vue 真题演练
- 看红框内的data,函数return 一个对象,
看似
export default是一个对象,实际上
.vue文件在被编译出来以后,这个vue实际上是一个class是一个类,vue组件是一个class,在每个地方使用这个组件时,相当于对class进行了一个实例化,在实例化时执行这个data,如果这个data不是一个函数的话,那每一个组件的实例都一样了就共享了,那就麻烦了。
-
如果在这个组件实例化一个,在其他组件实例化一个,
如果data不是函数的话,那在这个组件改了
data中的name,那其他组件的name也会随之改变。
- 所以说如果 data是一个函数,那在左边实例化一个,右边实例化一个,它都会执行这个函数,两个data都会在闭包当中,所以说两个杆(组件)就不会相互影响。其中一个data内容改变不会影响其他组件中的data。因此data必须是一个函数。