小兔鲜儿项目(vue3)注意点总结

1,081 阅读9分钟

第一板块

自动引入 less 变量到组件中

yarn add style-resources-loader

yarn add vue-cli-plugin-style-resources-loader
  1. 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 | 加载某些数据 | 无返回值或者返回是否加载完成的结果

匈牙利简版命名法

类型前缀类型实例
数组aArrayaItems(项目)
字符串sStringsUserName
函数fnFunctionfnHandler(处理程序)
对象oObjectop
整数iIntegeriItemCount(项目计数)
浮点数fFloatfPrice(价格)
布尔值bBooleanbIsComplete(完成)
正则表达式reRegExpreEmailCheck(邮件检测)

注意

  1. 在 vue3 中要在组件中使用store ,需用从 vuex 模块按需导入 userStore(组件中的使用方式), 然后创建 store 实例

image.png

  1. 如果不在组件中使用 store, 就需要 import store from '@/store' 导入 store 实例

const routes = [
  {
    path: '/',
    component: Layout,
    children: [
      {
        path: '',
        component: () => import('@/views/Home')
      }
    ]
  }
]
  • 这里静态路由的设置中 children 是一个数组,
  • 子路由中的 component 是一个箭头函数, 直接 import 需要导入的路径

补充

  1. 骨架屏: 在页面还未请求到数据没有渲染页面时, 用默认的样式先占位, 用来增强用户体验

    image.png

第二板块

懒加载

组件数据懒加载

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是否进入视口区域的监听会一直进行不会只监听一次
*/

图片懒加载

  1. vue2 和 vue3 全局指令注册的区别
// vue2  
Vue.directive(指令名, {
	inserted (el, binding) {
        
	}
})

// vue3
app.directive(指令名, {
    // 指令所在的元素,dom结构被解析完成
    mounted (el, binding) {
        
    }
})

调用@vueuse/core 包中 方法, 并配合自定义指令, 在页面滑动到图片的位置再加载图片

image.png

vue3 中的插件注册

  1. 在 main.js 中利用 createApp 创建实例, 每次调用 use 也能返回实例, 所以可以用链式写法, 最后才是 dom 的挂载

image.png

  1. 通过 use 方法挂载的插件, 在 install 参数中能够拿到挂载位置的 app 实例, 并在 install 方法中对实例进行相应的操作(这里是对实例进行自定义指令的注册, 并且是全局指令的注册)

image.png

  • 注意这里的 el.onerror 能够在元素没有正常加载时执行的函数, 能够处理失效图片的情况, 用默认图片进行替代

组件缓存 和 路由缓存

路由缓存

  • 现存问题:由于路由的组件复用机制,当配置路由的路径path没有发生变化时,路由对应的组件会被直接复用 (解决路由切换,组件复用时,数据不重新获取的问题)

  • 也就是生命周期钩子函数不再执行,请求也就不会再次发起

  • 解决1:给router-view 加上key 值为每次路由切换的完整路径 (<router-view :key="$route.fullPath"></router-view>)

  • 解决2: 或者利用 watch 监听 route 的变化, 如果路由有变化, 那么就重新发请求获取数据

继承

  1. 构造函数继承, 继承的是属性

image.png

  • 一般挂载方法不会往实例上挂, 因为这样会浪费内存, 所以一般放在原型中
  • 实例才会访问原型, 所以这里 23 行代码
  1. 原型的继承, 能够继承方法

image.png

  1. 组合继承 (构造函数能继承属性 + 原型继承能继承方法)

image.png

  1. 注意这种方式的赋值, 不叫继承, 会造成两个构造函数的数据相互影响

image.png

注意

  1. slot 插槽上如果没有 name 属性, 那么默认的 name 值为 default, 也就是说
  2. vuex 的 js 文件中的 this 指向的是 store

补充

  1. typeof 来判断引用类型变量时,无论是什么类型的变量,它都会返回 Object, 为此,引入了instanceof
  2. console.log 的打印是瞬时的快照, 如果在打印代码之后对于引用类型数据有数据的修改, 那么展开数据时,它其实是重新去内存中读取对象的属性值
  3. 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 代码实现动画

image.png

补充

  1. image.png

  2. 组件标签有个属性 is, 给属性值绑定的组件名字就能显示相对应的组件 <component :is="comName"></component>

image.png

注意

  1. 组件中类似 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 代码代替原来的指令封装的逻辑

image.png

<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>
  1. jsx 中的变量都需要用一层大括号包起来
  2. 如果在其中要写注释, 也需要用 大括号 包起来
  3. 这里循环生成标签利用 map 进行循环渲染
<a
            href="javascript:;"
            key={item.props.name}
            class={{ active: this.modelValue === item.props.name }}
            onClick={() => this.handleClick(item)}
          >
            {item.props.label}
          </a>
  1. jsx 中的 onclick 触发的函数如果要进行传参就要写成这种形式, 不能写成 onclick={this.handleClick(item)}, 因为这样就会在没有点击的时候直接调用
  2. 如果不用传参, 就可以用 onclick={this.handleClick}
  3. 这里的 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查找:发现可以找到,说明为服务端渲染. 一般的用户评论为客户端渲染。

  1. 客户端渲染需要对服务端进行两次请求,响应的开销较大,而服务端渲染只需要客户端对服务端进行一次请求

  2. 如何查看一个网页是客户端渲染还是服务端渲染:可以通过右键查看源代码的形式

    客户端渲染: 右击查看源代码找不到内容

    服务段渲染:是可以在源代码中找到内容的

  3. 网站一般都是用客户端渲染和服务端渲染结合的形式

  4. 正真的网站既不是纯异步,也不是纯服务端渲染,而是两者结合,

  5. 商品的商品列表采用的是服务端渲染,目的是为了SEO搜索引擎优化,而他的商品评论为了用户体验,用户体验更好

  6. 服务端渲染可以被爬虫抓取到,客户端渲染爬虫抓取不到

补充

  1. provide 和 inject 的用法: provide 在父组件中使用, inject 在子组件中使用, 前提是组件在构建时形成的就是子父级的关系(就是父子组件标签嵌套的形式)
  2. 在 render 函数中能够使用 this, 能够拿到 props 和 setup 中的变量和方法
  3. 与函数调用一起使用时,如果给定的函数不存在,则返回 undefined (括号中的 ?. , 变为返回 undefined 而不是直接报错 , 下面的 if 就是说如果找不到最终的 name, === 左边这一坨就返回 undefined )
if (item.children && item.children[0]?.type?.name === 'XtxTabsPanel') {
  console.log(item)
  item.children.forEach((com) => {
    panels.push(com)
  })
}

注意

  1. 注意这里的 toRefs 是为了解决解构赋值脱离响应式的问题, 进行响应式数据绑定

image.png