第一板块
自动引入 less 变量到组件中
yarn add style-resources-loader
yarn add vue-cli-plugin-style-resources-loader
- vue.config.js 中配置
const path = require('path')
module.exports = {
pluginOptions: {
'style-resources-loader': {
preProcessor: 'less',
patterns: [
// 配置哪些文件需要自动导入
path.join(__dirname, './src/styles/variables.less')
]
}
}
}
命名规范小结
函数命名
- 命名方法:小驼峰式命名法。
- 命名规范:前缀应当为动词。
- 命名建议:可使用常见动词约定 动词 | 含义 | 返回值 | | ---- | --------------- | ------------------------------- | | can | 判断是否可执行某个动作(权限) | 函数返回一个布尔值。true:可执行;false:不可执行 | | has | 判断是否含有某个值 | 函数返回一个布尔值。true:含有此值;false:不含有此值 | | is | 判断是否为某个值 | 函数返回一个布尔值。true:为某个值;false:不为某个值 | | get | 获取某个值 | 函数返回一个非布尔值 | | set | 设置某个值 | 无返回值、返回是否设置成功或者返回链式对象 | | load | 加载某些数据 | 无返回值或者返回是否加载完成的结果
匈牙利简版命名法
| 类型 | 前缀 | 类型 | 实例 |
|---|---|---|---|
| 数组 | a | Array | aItems(项目) |
| 字符串 | s | String | sUserName |
| 函数 | fn | Function | fnHandler(处理程序) |
| 对象 | o | Object | op |
| 整数 | i | Integer | iItemCount(项目计数) |
| 浮点数 | f | Float | fPrice(价格) |
| 布尔值 | b | Boolean | bIsComplete(完成) |
| 正则表达式 | re | RegExp | reEmailCheck(邮件检测) |
注意
- 在 vue3 中要在组件中使用store ,需用从 vuex 模块按需导入 userStore(组件中的使用方式), 然后创建 store 实例
-
如果不在组件中使用 store, 就需要
import store from '@/store'导入 store 实例
const routes = [
{
path: '/',
component: Layout,
children: [
{
path: '',
component: () => import('@/views/Home')
}
]
}
]
- 这里静态路由的设置中 children 是一个数组,
- 子路由中的 component 是一个箭头函数, 直接 import 需要导入的路径
补充
-
骨架屏: 在页面还未请求到数据没有渲染页面时, 用默认的样式先占位, 用来增强用户体验
第二板块
懒加载
组件数据懒加载
import { useIntersectionObserver } from '@vueuse/core'
const { stop } = useIntersectionObserver(
target,
([{ isIntersecting }]) => {
if(isIntersecting){ 进入区域时要进行的操作 }
}
)
/*
1.stop 一个可执行的函数用来停止监听行为
2.target 一个由ref api调用之后形成的RefImpl对象 也可以是一个dom对象 (注意这里需要给要懒加载组件的标签上设置 ref = 'target' )
3.isIntersecting 一个类型为布尔值的数据 当被监听元素进入视口区域时为true,离开视口区域时为false
特别注意: 对于目标target是否进入视口区域的监听会一直进行不会只监听一次
*/
图片懒加载
- vue2 和 vue3 全局指令注册的区别
// vue2
Vue.directive(指令名, {
inserted (el, binding) {
}
})
// vue3
app.directive(指令名, {
// 指令所在的元素,dom结构被解析完成
mounted (el, binding) {
}
})
调用@vueuse/core 包中 方法, 并配合自定义指令, 在页面滑动到图片的位置再加载图片
vue3 中的插件注册
- 在 main.js 中利用 createApp 创建实例, 每次调用 use 也能返回实例, 所以可以用链式写法, 最后才是 dom 的挂载
- 通过 use 方法挂载的插件, 在 install 参数中能够
拿到挂载位置的 app 实例, 并在 install 方法中对实例进行相应的操作(这里是对实例进行自定义指令的注册, 并且是全局指令的注册)
- 注意这里的 el.onerror 能够在元素没有正常加载时执行的函数, 能够处理失效图片的情况, 用默认图片进行替代
组件缓存 和 路由缓存
路由缓存
-
现存问题:由于路由的组件复用机制,当配置路由的路径path没有发生变化时,路由对应的组件会被直接复用 (解决路由切换,组件复用时,数据不重新获取的问题)
-
也就是生命周期钩子函数不再执行,请求也就不会再次发起
-
解决1:给
router-view加上key 值为每次路由切换的完整路径 (<router-view :key="$route.fullPath"></router-view>) -
解决2: 或者利用 watch 监听 route 的变化, 如果路由有变化, 那么就重新发请求获取数据
继承
- 构造函数继承, 继承的是属性
- 一般挂载方法不会往实例上挂, 因为这样会浪费内存, 所以一般放在原型中
- 实例才会访问原型, 所以这里 23 行代码
- 原型的继承, 能够继承方法
- 组合继承 (构造函数能继承属性 + 原型继承能继承方法)
- 注意这种方式的赋值, 不叫继承, 会造成两个构造函数的数据相互影响
注意
- slot 插槽上如果没有 name 属性, 那么默认的 name 值为 default, 也就是说
- vuex 的 js 文件中的 this 指向的是 store
补充
- typeof 来判断引用类型变量时,无论是什么类型的变量,它都会返回
Object, 为此,引入了instanceof - console.log 的打印是瞬时的快照, 如果在打印代码之后对于引用类型数据有数据的修改, 那么
展开数据时,它其实是重新去内存中读取对象的属性值 - auguments 是一个伪数组, 是所有实参的集合 (能够拿到函数传来的所有参数, 以伪数组的形式呈现)
- 可以通过展开运算符转换成真数组, 或者通过
Array.prototype.slice.call(伪数组)转真数组 - 注意箭头函数没有 auguments, 只能通过展开运算符拿到
(...args) => {}
第三板块
v-model 绑定的数据(父子组件之间数据传递, 双向绑定的标准写法)
这里封装的
<XtxCheckbox v-model="form.isAgree"/>是一个复选框按钮, 能够进行双向数据绑定. 子组件中通过 props 接收 modelValue 数据, 通过 emit 将数据传给父组件, 注意这里还有个操作: 就是利用 watch 拿到父组件的初始值(这里的 watch 的数据是基本数据类型, 所以需要写在函数中)
<template>
<div class="xtx-checkbox" @click="toggleCheck">
<!-- 选中小图标 -->
<i v-if="checked" class="iconfont icon-checked"></i>
<!-- 未选中小图标 -->
<i v-else class="iconfont icon-unchecked"></i>
<!-- 自定义文字 -->
<span><slot /></span>
</div>
</template>
<script>
import { ref, watch } from 'vue'
export default {
name: 'XtxCheckbox',
props: {
modelValue: {
type: Boolean
}
},
setup (props, { emit }) {
// checked 选中状态 true代表选中 false代表未选中
const checked = ref(false)
// 实现通过v-model 完成父子之间的选中状态同步
/*
1.绑定自定义属性 这个属性叫做modelValue props
2.绑定自定义事件 这个事件叫做update:modelValue emit('update:modelValue')
3.自定义事件绑定的回调函数中完成了对于自定义属性的赋值修改(这个值就是我们触发自定义事件时传递的实参)
*/
function toggleCheck () {
// 1. 完成状态的切换
checked.value = !checked.value
// 2. 切换完毕还需要把当前最新的状态抛给父组件
emit('update:modelValue', checked.value)
// 增加一个自定义事件 change
emit('change', checked.value)
}
watch(() => {
return props.modelValue
}, () => {
checked.value = props.modelValue
}, {
immediate: true
})
return {
checked,
toggleCheck
}
}
}
</script>
promise 方法的使用
第一种写法通过在函数中 return 一个 promise 对象, 作用和 async 一样, 然后要注意在 new Promise 中一定要出现 resolve(方法中不管包不包数据, 都需要出现, 作为当前 promise 回调函数的一个结束标志, 将 pending 状态修改为 resolve 状态, 没有的话就不能进行之后的代码)
// 删除购物车商品
asyncDeleteCart (context, goods) {
return new Promise((resolve, reject) => {
if (context.rootState.user.profile.token) {
// 已登录的删除
} else {
// 未登陆
context.commit('deleteCart', goods)
resolve()
}
})
},
- 推荐写法
// 删除购物车
async asyncDeleteCart (context, goods) {
// 分登陆和未登陆
if (context.rootGetters.token) {
await reqDeleteCart([goods.skuId])
await context.dispatch('asyncUpdateCart')
} else {
context.commit('deleteCart', goods)
}
},
利用 js 代码实现动画
补充
-
-
组件标签有个属性 is, 给属性值绑定的组件名字就能显示相对应的组件
<component :is="comName"></component>
注意
- 组件中类似
const store = useStore()的实例创建, 只能写在 setup 的代码中, 不能写在 setup 的函数中
第四板块
template 和 render 挂载 dom 元素
template 中只能用 vue 特定的语法逻辑来渲染, 但是在 render 函数中写代码能用 js 语法写, 能够实现更加灵活的 dom 渲染, 但是不易理解, 所有可以利用 jsx 语法配合 render 函数书写代码
<script>
import { createVNode } from 'vue'
export default {
name: 'XtxHello',
render() {
const div = createVNode('div', null, 'Hello World')
return div
}
}
</script>
了解 jsx 语法
在 render 函数中可以写 jsx 代码, 就解决了直接通过在 render 函数中写 createVNode 难以理解的问题, react 语法核心就是 jsx 语法, 对写原生 js 代码的要求更高, 就是说不会像 vue 中有那么多命令可以用, 这里就需要自己写敲 js 代码代替原来的指令封装的逻辑
<script>
export default {
name: 'XtxHello',
render() {
const isLoading = false
const obj = { name: 'ifer', age: 18, likes: ['吃饭', '睡觉', '写代码'] }
const handleClick = () => {
console.log('~~~')
}
if (isLoading) {
return <div>loading...</div>
}
return (
<div>
{/* #1 输出 */}
<p>name: {obj.name}</p>
<p>age: {obj.age}</p>
{/* #2 遍历 */}
<ul>
{obj.likes.map((item) => (
<li key={item}>{item}</li>
))}
</ul>
{/* #3 事件 */}
<button onClick={handleClick}>点击</button>
</div>
)
}
}
</script>
- jsx 中的变量都需要用一层
大括号包起来 - 如果在其中要写注释, 也需要用 大括号 包起来
- 这里循环生成标签利用 map 进行循环渲染
<a
href="javascript:;"
key={item.props.name}
class={{ active: this.modelValue === item.props.name }}
onClick={() => this.handleClick(item)}
>
{item.props.label}
</a>
- jsx 中的 onclick 触发的函数如果要进行
传参就要写成这种形式, 不能写成onclick={this.handleClick(item)}, 因为这样就会在没有点击的时候直接调用 - 如果
不用传参, 就可以用onclick={this.handleClick} - 这里的 class 要写在对象中, (两层大括号: 外层大括号是为了写变量, 里层大括号是要写 class 的语法)
浏览器渲染过程
- 解析URL
- 浏览器本地缓存
- DNS解析
- 建立TCP/IP连接
- 发送HTTP请求
- 服务器处理请求并返回HTTP报文
- 浏览器根据深度遍历的方式把html节点遍历构建DOM树
- 遇到CSS外链,异步加载解析CSS,构建CSS规则树
- 遇到script标签,如果是普通JS标签则同步加载并执行,阻塞页面渲染,如果标签上有defer / async属性则异步加载JS资源
- 将dom树和CSS DOM树构造成render树
- 渲染render树
服务端渲染 和 客户端渲染
右击查看网页源代码,ctrl+f查找:发现可以找到,说明为服务端渲染. 一般的用户评论为客户端渲染。
-
客户端渲染需要对服务端进行两次请求,响应的开销较大,而服务端渲染只需要客户端对服务端进行一次请求
-
如何查看一个网页是客户端渲染还是服务端渲染:可以通过右键查看源代码的形式
客户端渲染: 右击查看源代码找不到内容
服务段渲染:是可以在源代码中找到内容的
-
网站一般都是用客户端渲染和服务端渲染结合的形式
-
正真的网站既不是纯异步,也不是纯服务端渲染,而是两者结合,
-
商品的商品列表采用的是服务端渲染,目的是为了SEO搜索引擎优化,而他的商品评论为了用户体验,用户体验更好
-
服务端渲染可以被爬虫抓取到,客户端渲染爬虫抓取不到
补充
- provide 和 inject 的用法: provide 在父组件中使用, inject 在子组件中使用, 前提是组件在构建时形成的就是子父级的关系(就是父子组件标签嵌套的形式)
- 在 render 函数中能够使用 this, 能够拿到 props 和 setup 中的变量和方法
- 与函数调用一起使用时,如果给定的函数不存在,则返回
undefined(括号中的?., 变为返回 undefined 而不是直接报错 , 下面的 if 就是说如果找不到最终的 name, === 左边这一坨就返回 undefined )
if (item.children && item.children[0]?.type?.name === 'XtxTabsPanel') {
console.log(item)
item.children.forEach((com) => {
panels.push(com)
})
}
注意
- 注意这里的 toRefs 是为了解决解构赋值脱离响应式的问题, 进行响应式数据绑定