20230714----重返学习-DOM-diff算法-构建工具-包管理工具-Vite基本使用-Vue3新特性

news2025/1/19 11:08:41

day-112-one-hundred-and-twelve-20230714-DOM-diff算法-构建工具-包管理工具-Vite基本使用-Vue3新特性

DOM-diff算法

vue2中diff算法

  • 同级比对,跨级比对性能差。而且采用的方式是递归比对,更差一点。
  • 根节点只能有一个,比对的时候会从根节点进行比对,先判断两个根节点是否是同一个节点。
    • 如果不是同一个节点则直接将老节点删除掉,换成新节点。
      • 根据标签名、key。isSameVnode()这个方法来判断是否是相同的同一个节点。
    • 如果两个节点的key和标签名一致-是相同节点。不过还要判断该相同节点的属性与子元素是否已经改变。
      • 虚拟DOM的比对是:
      • 第一次渲染的时候会根据虚拟DOM创建一个真实DOM。
      • 更新后会再次产生一个新的虚拟节点。如果isSameVnode为true,则复用之前的dom,比较前后两次节点的差异,最终更新到页面上。
        • 递归比较子节点:
          • 新儿子有,老儿子没有。根据新的虚拟节点创建真实节点插入到老节点中。
          • 老儿子有,新的儿子没有。删除老的儿子,移除DOM元素。
          • 两方都有。diff算法。
            • 先做了优化-双端比对。对DOM常见的操作做了优化。
              1. 常见操作:(增-向前向后添加一个、倒序正序、删除)
              2. diff算法中key的作用:用来识别dom元素的,如果一致的key和tag会认为是同一个元素,在使用的时候key不要不写,或者采用索引。
              • 给新老节点增加头尾指针-默认从头部开始比对,头指针比对成功后会向后移动,尾指针比对后会向前移动。
                • 如果比对成功后指针向后移动,继续进行比对,如果多出来的则插入到老节点中。
              • 如果头部指针无法比对成功,则从尾部进行比较。删除也可以通过这种方式进行比较操作。
              • 针对尾部移动到头部来说,如果头和头、尾和尾部都不行,比较尾部和头部,比对成功将尾部移动到头部去,并且继续。
                • 头和头、尾和尾都不行的情况下…。
              • 针对尾部移动到头部来说,和上面是一致的。
              • 如果没有走到优化策略,则会采用暴力比对的方式。将新节点的每一个拿出来和老节点去比对,如果找到了则复用,并且移动,如果找不到则直接插入到头指针的前面。每一轮都要经历头头、尾尾、头尾、尾头,最终老的多的删掉,新的多的则创建并插入。
          • 老儿子是文本,新的儿子也是文本。可以用新的文本换掉老的文本。

实际比对流程

  • 先比较是否是相同节点 (isSameVNode)。
    • 如果是相同节点,则比较属性,并复用老节点(将老的虚拟 dom 复用给新的虚拟节点 DOM)。
      1. 比较属性并进入下一步:
        • 属性不相同,用新的属性替换旧的属性。
        • 属性相同,则不更新。
      2. 比较儿子节点:
        • 老的没儿子,新的有儿子。直接插入新的儿子。
        • 老的有儿子,新的没儿子。直接删除页面节点。
        • 老的儿子是文本,新的儿子是文本,直接更新文本节点即可。
        • 老的有儿子,新的也有儿子。
          • 老的儿子是一个列表,新的儿子也是一个列表 updateChildren。
            1. 先做了优化-双端比对。对DOM常见的操作做了优化。
              • 每次都从老新儿子列表中各自拿出一个值来进行比较。
                1. 老节点列表的头部新节点列表的头部进行比较。
                  • 结果相同:老节点列表的头部指针向后移动一位,新节点列表的头部指针向后移动一位。跳出本轮次,进行下一轮次的比较。
                  • 结果不同,进入本轮次第2步。
                2. 老节点列表的尾部新节点列表的尾部进行比较。
                  • 结果相同:老节点列表的尾部指针向前移动一位,新节点列表的尾部指针向前移动一位。跳出本轮次,进行下一轮次的比较。
                  • 结果不同,进入本轮次第3步。
                3. 老节点列表的头部新节点列表的尾部进行比较。
                  • 结果相同:老节点列表的头部指针向后移动一位,新节点列表的尾部指针向前移动一位,移动节点-具体个人不太清楚。跳出本轮次,进行下一轮次的比较。
                  • 结果不同,进入本轮次第4步。
                4. 老节点列表的尾部新节点列表的头部进行比较。
                  • 结果相同:老节点列表的尾部指针向前移动一位,新节点列表的尾部指针向前后动一位,移动节点-具体个人不太清楚。跳出本轮次,进行下一轮次的比较。
                  • 结果不同,进入本轮次第5步。
                5. 采用暴力比对的方式。将新节点的每一个拿出来和老节点去比对,如果找到了则复用,并且移动,如果找不到则直接插入到头指针的前面。这一步必定可以解决,进入下一轮次。
          • vue-diff列表两两比较.jpg
    • 如果不是相同节点,则新创建一个元素。
  1. Vue2的diff算法–updateChildren图文流程以及缺点

key的作用

  • key 的特殊 attribute 主要用在 Vue 的虚拟 DOM 算法,在新旧 nodes 对比时辨识 VNodes。如果不使用 key,Vue 会使用一种最大限度减少动态元素并且尽可能的尝试就地修改/复用相同类型元素的算法。

  • 当 Vue 正在更新使用 v-for 渲染的元素列表时,它默认使用“就地更新”的策略。如果数据项的顺序被改变,Vue 将不会移动 DOM 元素来匹配数据项的顺序,而是就地更新每个元素,并且确保它们在每个索引位置正确渲染。

  • Vue 在 patch 过程中通过 key 可以判断两个虚拟节点是否是相同节点。 (可以复用老节点)

  • 无 key 会导致更新的时候出问题,尽量不要采用索引作为 key。

<script src="https://cdn.jsdelivr.net/npm/vue@2"></script>
<div id="app">
    <li v-for="item in list" :key="item">
        <input type="checkbox"> {{item}}
    </li>
    <button @click="add">增加</button>
</div>
<script>
    const vm = new Vue({
        el: '#app',
        data: {
            list: [1, 2, 3, 4]
        },
        methods: {
            add() {
                this.list.unshift(Math.random())
            }
        }
    })
</script>
  • 有key,会在前面新增,效果与预期一致。
<script src="https://cdn.jsdelivr.net/npm/vue@2"></script>
<div id="app">
    <li v-for="item in list" :key="item">
        <input type="checkbox"> {{item}}
    </li>
    <button @click="add">增加</button>
</div>
<script>
    const vm = new Vue({
        el: '#app',
        data: {
            list: [1, 2, 3, 4]
        },
        methods: {
            add() {
                this.list.unshift(Math.random())
            }
        }
    })
</script>
  • 无key或key为index,会在后面新增,效果与预期不符,勾选的选项会错位。
  • key
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <div id="app">
      <li v-for="(item,index) in list"><input type="checkbox" />{{item}}</li>
      <button @click="unshift()">没有key时。向前追加,`a、b、c、d`会复用成`e、a、b、c`,新增的DOM给到了d-而不是我们预期的e</button>
      <button @click="reverse()">倒序让子元素变动了三次,不过DOM都复用了</button>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.7.14"></script>
    <script>
      const vm = new Vue({
        el: "#app",
        data() {
          return {
            list: ["a", "b", "c", "d"],
          };
        },
        methods: {
          reverse() {
            this.list.reverse();
          },
          unshift() {
            this.list.unshift("e");
          },
        },
      });
    </script>
  </body>
</html>

数组的数据劫持

  1. 为什么数据不能采用Object.defineProerty(),因为数据的length可以设置得很大,进而劫持的时候浪费性能。
    • 虽然Object.defineProerty()可以监听到数组的角标,但由于路由的length可以设置成很大。如Object.defineProerty(new Array(10000)),会监听很多的的属性,设置很多的监听器,进而让性能浪费。
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <script>
      // 设计模式:工厂模式、单例、 发布订阅、 观察者模式、策略模式, 代理模式

      // 为什么数组不能采用Object.defineProperty 因为劫持的时候浪费性能
      // 数组采用重写方法的方式

      // 数组不能采用Object.defineProerty()。虽然Object.defineProerty()可以监听到数组的角标,但由于路由的length可以设置成很大。如Object.defineProerty(new Array(10000)),会监听很多的的属性,设置很多的监听器,进而让性能浪费。
      const arr = ['a','b','c'];
      for(let key in arr){
          Object.defineProperty(arr,key,{
              get(){

              },
              set(){

              }
          })
      }
      console.log(arr)
    </script>
  </body>
</html>

响应式数据的原理

  • 具体流程:
  1. 渲染的时候会去调用render函数-每个组件都有一个watcher,当渲染的时候会将这个watcher放到全局上。
  2. 调用render方法时候会去取值,每个属性都有一个dep数组,只要取值就让这个dep数组
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <script>
      // 设计模式:工厂模式、单例、 发布订阅、 观察者模式、策略模式, 代理模式


      // 发布订阅模式(我们会把事件订阅好,后续自己来触发 【f,f,f,f】)
      // 观察者模式(包含发布订阅模式)  一个小宝宝会记录自己父亲和母亲,等会状态变化了会主动通知父亲和母亲

      function render() {
        // 页面渲染的时候会取取响应式数据
        console.log(state.name);
      }

      class Dep {
        constructor() {
          this.set = new Set(); // dep里面存的是watcher
        }
        depend() {
          this.set.add(Dep.target); // 属性和watcher产生关联
        }
        notify() {
          this.set.forEach((watcher) => watcher.update());
        }
      }
      Dep.target = null; // 默认没有任何的watcher正在执行

      class Watcher {
        // 页面的渲染逻辑
        constructor(getter) {
          this.render = getter;
          this.get();
        }
        get() {
          Dep.target = this;
          this.render();
          Dep.target = null;
        }
        update() {
          this.render();
        }
      }

      // 1.渲染的时候会去调用render函数 (每个组件都有一个watcher,当渲染的时候会将这个watcher放到全局上)
      // 2.调用render方法时候会去取值, 每个属性都有一个dep,只要取值就让这个dep记住这个watcher

      // 对数据进行数据劫持的目的 : 能监控用户的取值和设置值的操作。
      function isObject(data) {
        return data !== null && typeof data === "object";
      }
      function defineReactive(target, key, value) {
        const dep = new Dep();
        observe(value);
        Object.defineProperty(target, key, {
          get() {
            if (Dep.target) {
              // 在组件中渲染的
              dep.depend(); // 让这个属性的dep,去记录他对应的是哪个组件
            }
            return value;
          },
          set(newValue) {
            if (newValue === value) return;
            observe(newValue);
            dep.notify();
            value = newValue;
          },
        });
      }
      function observe(data) {
        if (!isObject(data)) {
          return;
        }
        for (let key in data) {
          defineReactive(data, key, data[key]);
        }
      }
      const state = { name: "zf", age: { num: 100 } };
      observe(state);

      // vue中的特点叫组件及更新 (更新是以组件为单位的)
      debugger;
      new Watcher(render);
      debugger;
      state.name = "jw";

      // 作业:总结diff算法, key的作用, 依赖收集的流程
    </script>
  </body>
</html>
  • 每个属性都拥有自己的dep属性,存放他所依赖的 watcher,当属性变化后会通知自己对应的 watcher 去更新
  • 默认在初始化时会调用 render 函数,此时会触发属性依赖收集 dep.depend
  • 当属性发生修改时会触发watcher更新 dep.notify()

img

class Dep {
    constructor() {
        this.subs = new Set;
    }
    depend() {
        this.subs.add(Dep.target); // 让属性记住这个watcher
    }
    notify() {
        this.subs.forEach(watcher => watcher.update()); // 通知记住的watcher更新
    }
}
class Watcher{
    constructor(fn) {
        this.getter = fn;
        this.get();
    }
    get() { // 第一次渲染
        Dep.target = this;
        this.getter();
        Dep.target = null;
    }
    update() { // 数据变化后更新
        this.get();
    }
}
function defineReactive(obj, key, value) {
    const dep = new Dep();
    Object.defineProperty(obj, key, {
        get() {
            if (Dep.target) { // 说明是在watcher中访问的属性
                dep.depend()
            }
            return value;
        },
        set(newValue) {  // 如果设置的是一个对象那么会再次进行劫持
            if (newValue === value) return
            observe(newValue);
            value = newValue
            dep.notify();
        }
    })
}
function isObject(value) {
    return typeof value === 'object' && value !== null;
}
function observe(value) {
    if(!isObject(value)){
        return;
    }
    Object.keys(value).forEach(key=>{ // 要使用defineProperty重新定义
        defineReactive(value,key,value[key]);
    });
}
const state = {name:'jw'}
observe(state); // 观测状态,在组件渲染时使用此状态
function render() { 
    console.log(state.name)
}
new Watcher(render);

vue响应式数据的特点

  1. vue中的特点是组件级更新,更新是以组件为单位的。

设计模式

  • 设计模式:
  1. 工厂模式、单例设计模式、发布订阅、观察者模式、策略模式、代理模式。

作业

  1. 总结diff算法、key的作用、依赖收集的流程。

构建工具

  • webpack特点:
    • 很强大,功能丰富,生态好,很多插件,webpack启动是比较慢的,内部会将所有的模块进行打包,打包后再启动服务,热更新,随着项目的增大依旧会卡。
      • 如果项目特别大,在最终打包的时候也是很大的。
        • 有方案来解决:
          • 微前端,将一个项目拆分成多个子项目,最终再组件在一起。
            • 微前端的好处就是每个子项目是一个独立的项目技术栈可以不同,最后组合在一起。
      • webpack比较强大,可以处理各种资源。可以使用ES6Module的import、nodejs的require、多线程打包插件。
  • rollup他的目的主要就是打包类库(react源码、vue源码都是采用的rollup) 打包速度快、只支持esModule,可以更好地支持tree-shaking。而且生态也很好,用起来比webpack更简单-支持插件。
  • gulp 通过流的方式对资源进行转换-不用全部打包在一起,通常在写组件库中会使用。
  • esbuild 使用的是go语言编写的,用于并行打包资源,整个都进行了重写,内置了一些打包需要用的插件。但只适合开发环境,生产环境不够完善。内部也支持插件,但由于种种的原因,没有在生产环境下使用。
  • parcel 打包工具,基本没什么人用。

高版本浏览器内置支持的ESMoule

  • index.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <script type="module">
        import a from './a.js'
        console.log(a)
    </script>
</body>
</html>
  • a.js
export default {
    name:'zf'
}
  1. 其中其中a.js就是用ESModule来导入的。它的运行也是ES6模块的运行环境。

vite

  • vite内部会对代码进行分类,源码与第三方库,自己写的代码。

  • 开发的时候vite采用esbuild来进行开发环境。它采用esbuild来做一些开发所需的第三方依赖来打包-预编译,放在那里。打包的结果到时候请求的时候资源小,而且会对这些模块进行合并以便减少请求。

  • 后续的源码通过浏览器内置的esModule来处理。不过开发环境下需要使用高版本浏览器-即支持esModule的浏览器。

  • vite只需要快速的启动一个本地服务即可,其它的操作都交给浏览器来处理。请求的时候再对这个文件做编译,如:请求xxx.vue这类文件要转成js,而浏览器实际请求到的也是js文件。

  • vite冷启动非常快。

  • 优化:代码体积,性能好,按需加载(依赖于浏览器内置的ESModule的特性)。

  • 对于生产环境而言:生产环境采用了rollup性能还不错,rollup打包出来的资源小。rollup天生支持esModule,对tree-shaking支持的也比较好。插件写起来也方便。

包管理工具

  • npm vs yarn vs pnpm
    • npm包管理工具(慢)
    • yarn需要用npm安装(无法干掉npm),yarn增加了很多功能。在一个包下管理多个包
    • pnpm 软链接,安装速度快。

vite项目

vite介绍

  • Vite(法语意为 “快速的”,发音 /vit/,发音同 “veet”)是一种新型前端构建工具,能够显著提升前端开发体验。它主要由两部分组成:
    • 一个开发服务器,它基于原生 ES 模块提供了丰富的内建功能,如速度快到惊人的模块热更新(HMR)(在开发环境中会基于ES模块启动一个本地服务,优势是可以进行按需加载。并且可以实时预览开发的内容)
    • 一套构建指令,它使用 Rollup 打包你的代码,并且它是预配置的,可输出用于生产环境的高度优化过的静态资源。(生产环境是基于rollup来进行打包,提供了内置配置,最终打包出来的资源小。可直接部署到服务器上)

vite与webpack的对比

开发环境对比
  • Webpack:冷启动(首次启动)开发服务器时,需要构建完整的应用,之后才能提供开发服务。(大型项目启动时非常缓慢,后续更新也非常缓慢) Webpack中大量插件由第三方编写,可能会有性能差等问题。
  • Vite:冷启动时,不需要等待构建完整的应用 (vite支持预构建) Vite使用esbuild对第三方模块进行预构建,将非ESM规范的代码转换为ESM规范的代码,同时可以将多个文件资源合并成一个,减少http请求。(Vite是基于ESModule的) 在服务启动后浏览器会根据入口文件需要发送相应请求,获取资源时对代码进行转化等操作。

快速的冷启动、实时模块转换。

image-20230711150202827

image-20230711150407049

esbuild

image-20230711150803107

  • 基于 Go 语言:ESBuild 是使用 Go 语言编写的构建工具。Go 语言可以充分利用多核CPU 实现并行构建。
  • esbuild专注js、ts、jsx的构建和转译,使用自己的算法进行转换和优化,相比其他打包工具速度更快。
  • esbuild同样也具有插件系统,可以扩展其自身功能。

但是目前esbuild在构建方面表现出色,但是在生产环境中还不够完善。生产环境我们更关心:代码体积、代码分割、代码缓存等。 Vite在生产环境中采用Rollup的目的也是认为 Rollup 提供了更好的性能与灵活性方面的权衡。

生产环境对比
  • Webpack 是一个非常成熟和广泛采用的打包工具,拥有庞大的生态系统和丰富的插件支持,对于大型复杂的项目,Webpack 的成熟生态系统和丰富的功能一定为首选。
  • 对于Vite而言,生产采用的是rollup。
    • 体积更小:Rollup专注于 JavaScript 模块的打包,可以生成更小、更精简的输出文件,它提供了更好的 tree shaking(摇树优化)功能。
    • 更快的构建速度:Rollup在打包时的速度通常要比webpack更快。
    • 针对库的打包:Rollup更适合于构建独立的库或组件
    • ES模块的支持:Rollup天生支持ES模块的语法。
    • 插件系统:Rollup具有灵活的插件系统。

Vue2 一般采用webpack来处理,如果使用Vite还需要解决很多问题(老项目更不要在折腾了)。 Vue3 项目我们一般采用Vite来进行创建项目,当然也可以使用@vue/cli来进行创建。但要注意的是,ESModule需要浏览器天生支持才可以

Vite项目创建

node版本:v16.20.0

pnpm create vite
✔ Project name: … vue3-lesson   项目名
✔ Select a framework: › Vue     放在那个目录下
✔ Select a variant: › Customize with create-vue   创建什么框架的项目

Vue.js - The Progressive JavaScript Framework

✔ Add TypeScript? … No / Yes
✔ Add JSX Support? … No / Yes
✔ Add Vue Router for Single Page Application development? … No / Yes
✔ Add Pinia for state management? … No / Yes
✔ Add Vitest for Unit Testing? … No / Yes
✔ Add an End-to-End Testing Solution? › No
✔ Add ESLint for code quality? … No / Yes
✔ Add Prettier for code formatting? … No / Yes

vite的vue项目默认内置命令

  • 默认内置命令:
    • “dev”: “开发服务器”,
    • “build”: “打包”,
    • “preview”: “预览打包后文件”,
    • “lint”: “检测.vue,.js,.jsx,.cjs,.mjs --fix --ignore-path .gitignore的代码校验提示的”
    • “format”: “代码格式化,好处是可以在打包时,一次性把项目中的文件都格式化一下”
- 默认可执行命令
  - "dev": "vite", //开启开发环境
  - "build": "vite build", //打包项目生产 dist 目录
  - "preview": "vite preview", //预览 dist 目录
  - "lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs --fix --ignore-path .gitignore", //代码格式校验
  - "format": "prettier --write src/" //格式化代码的

目录结构

  • /.vscode/extensions.json 可以配置项目中使用的 vscode 插件 vue3 中需要使用 volar
  • /dist/ 打包出的结果
  • /public/ 公共的静态文件,这个目录会直接拷贝到 dist 目录下,不会被 vite 处理
  • /src/assets/ 这个目录打包的时候会进行编译 增加 hash 戳,压缩
  • /src/components/ 组件
  • /src/router/ 路由相关的配置
  • /src/views/ 页面级别组件
  • /src/App.vue 根组件
  • /src/main.js 入口文件 esMoudle 直接引入
  • /.eslintrc.cjs 主要是做 eslint 配置的
  • /.prettierrc.json 配置用户格式化的方式
  • /index.html 是整个入口文件

核心文件的目录层级结构及说明

  • .vscode:-> extensions.json 推荐安装插件
  • public:该目录通常用于存放不需要经过构建处理的静态资源。这些资源在打包过程中不会被处理或修改,直接复制到输出目录中
  • src:源代码目录
    • assets:通常用于存放需要经过构建处理的静态资源,这些资源在打包过程中会被构建工具所处理
    • components:项目中的公共组件
    • router:路由配置
    • views:页面及别组件
    • App.vue: 项目根组件
    • main.js:入口文件
  • .eslintrc.cjs:eslint配置文件,检测代码质量
  • .prettierrc.json: prettier插件,代码格式化
  • index.html:静态文件入口文件
  • vite.config.js:vite的配置文件

删除代码得到最简项目

  • .vscode:-> extensions.json 推荐安装插件

  • public:该目录通常用于存放不需要经过构建处理的静态资源。这些资源在打包过程中不会被处理或修改,直接复制到输出目录中

  • src:源代码目录

    • assets/

      • 一张图片。
    • router/:路由配置

      • index.js

        import { createRouter, createWebHistory } from "vue-router"
        const router = createRouter({
          history: createWebHistory(import.meta.env.BASE_URL),
          routes: [
        
          ]
        })
        export default router
        
    • App.vue: 项目根组件

      <script setup></script>
      <template>
        红色
      </template>
      <style scoped></style>
      
    • main.js:入口文件

      import { createApp } from 'vue'
      
      import App from './App.vue'
      import router from './router'
      const app = createApp(App)
      app.use(router)
      app.mount('#app')
      
  • .eslintrc.cjs:eslint配置文件,检测代码质量

  • .prettierrc.json: prettier插件,代码格式化

  • index.html:静态文件入口文件

    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="UTF-8">
        <link rel="icon" href="/favicon.ico">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Vite App</title>
      </head>
      <body>
        <div id="app"></div>
        <div id="root"></div>
        <script type="module" src="/src/main.js"></script>
      </body>
    </html>
    
  • vite.config.js:vite的配置文件

vite.config.js配置

// url模块 fileURLToPath 可以转化路径的
import { fileURLToPath, URL } from 'node:url'

// 在使用vite的时候写配置可以提供提示
import { defineConfig } from 'vite'

// @vitejs/plugin-vue 这个插件就是让vite能支持vue
// @vitejs/plugin-react
// @vitejs/plugin-vue-jsx 支持jsx 插件
import vue from '@vitejs/plugin-vue'
import vueJsx from '@vitejs/plugin-vue-jsx'

/* console.log(`import.meta.url-->`, import.meta.url)
console.log(
  `拼接好的本地文件地址:new URL('./src', import.meta.url)-->`,
  new URL('./src', import.meta.url)
)
console.log(
  `拼接好的绝对路径:fileURLToPath(new URL('./src', import.meta.url))-->`,
  fileURLToPath(new URL('./src', import.meta.url))
) */



// process.env.NODE_ENV
// import.meta.url
// https://vitejs.dev/config/


export default defineConfig({
  // vite的插件直接放在plugis中即可
  plugins: [vue(),vueJsx(),],
  resolve: {
    alias: {
      '@': fileURLToPath(new URL('./src', import.meta.url))
    }
  },
})

Vite基本使用

图片

  • 图片可以直接使用,还可以配置别名使用

    <template>
      <!-- webpack中想显示图片  file-loader url-loader;vite不用,它内置了 -->
      <img src="./assets/logo.svg" style="width: 100px; height: 100px" />
    </template>
    
    • vite-project/vite.config.js

      export default defineConfig({
        resolve: {
          alias: {
            '@': fileURLToPath(new URL('./src', import.meta.url))
          }
        },
      })
      
    • 示例:

      • vite-project/src/App.vue

        <template>
          <!-- webpack中想显示图片  file-loader url-loader -->
          <!-- alias -->
          <img src="@/assets/logo.svg" style="width: 100px; height: 100px" />
        </template>
        
      • vite-project/vite.config.js

        export default defineConfig({
          resolve: {
            alias: {
              '@': fileURLToPath(new URL('./src', import.meta.url))
            }
          },
        })
        

服务启动时引入一个静态资源会返回解析后的公共路径

<template>
  <img src="./assets/logo.svg" style="width: 100px;height: :100px;">
</template>

路径别名

<img src="@@/logo.svg" style="width: 100px;height: 100px;">
resolve: {
  alias: {
    '@@': fileURLToPath(new URL('./src/assets', import.meta.url))
  }
}

CSS

css module

  • vite-project/src/App.vue
<script setup>
import colors from "./var.module.css"
console.log(`colors-->`, colors);
</script>
<template>
  <!-- css module  css模块(文件后缀必须module.css) -->
  <span :class="colors.gold"> 金色 </span>
  <span :class="colors.fire"> 红色 </span>
</template>
  • vite-project/src/var.module.css
.gold {
  color: gold;
}
.fire {
  color: red;
}
.water {
  color: blue;
}

支持less和sass

  • css支持css module的写法,安装less和scss后可以直接使用。

    npm install lass
    
    npm install sass
    
    • scss用的还是sass这个依赖。
pnpm install less sass -D
  • vite-project/src/App.vue
<script setup>
</script>
<template>
  <span class="tree"> 绿色 </span>
  <span class="solid"> 土色 </span>
  <div class="box"></div>
  <van-field />
</template>

<style scoped>
.box {
  width: 100px;
  height: 100px;
  background: red;
  transform: rotate(45deg);
}
</style>

<style scoped lang="scss">
$color: green;
.tree {
  color: $color;
}
</style>
<style scoped lang="less">
@color: #ccc;

.solid {
  color: @color;
}
</style>

全局在scss中导入一行代码

  • vite-project/vite.config.js

    css: {
      preprocessorOptions: {
        scss: {
          additionalData: `@import "./assets/var.scss";`//就会在所有的scss文件中加入这样一行css代码。
        },
      }
    }
    
  • 所有预处理器选项还支持 additionalData 选项,可以用于为每个样式内容注入额外代码。请注意,如果注入的是实际的样式而不仅仅是变量时,那么这些样式将会在最终的打包产物中重复出现。

  • vite-project/vite.config.js

export default defineConfig({
  css: {
    preprocessorOptions: {
      scss: {
        additionalData: `@import "./assets/var.scss";`//就会在所有的scss文件中加入这样一行css代码。
      },
    }
  },
})

  • vite-project/src/assets/var.scss
$color: green;
  • vite-project/src/App.vue
<script setup>
</script>
<template>
  <span class="tree"> 绿色 </span>
</template>

<style scoped lang="scss">
// 每个scss代码块中都会注入这个公共的倒入模式
.tree {
  color: $color;
}
</style>

项目基础路径

  • 如可以用cdn项目根目录配置
export default defineConfig({
  base:'http://www.fulljs.cn/' // 基础路径
});

postcss自动加前缀

  1. 安装autoprefixer
npm install autoprefixer --save
  1. 配置相关的文件
  • vite-project/postcss.config.js
// 和 webpack配置一模样一样
module.exports = {
  plugins: [require('autoprefixer')]
}
  • vite-project/.browserslistrc
> 0.1%
  • vite-project/src/App.vue
<script setup>

</script>
<template>
  <div class="box"></div>

    <van-button>按钮</van-button>
  <van-field />
</template>

<style scoped>
.box {
  width: 100px;
  height: 100px;
  background: red;
  transform: rotate(45deg);
}
</style>

反向代理

server: {
  proxy: {
    '/api': {
      target: "http://jsonplaceholder.typicode.com",
      changeOrigin: true,
      rewrite:path => path.replace(/^\/api/,'')
    }
  }
}
fetch('/api/posts/').then(res=>json()).then(data => {
  console.log(data)
});

mock数据模拟

pnpm i mockjs vite-plugin-mock -D

纯反向代理

  • vite-project/vite.config.js
import { defineConfig } from 'vite'
export default defineConfig({
  server: {
    proxy: {
      '/api': {
        target: "http://jsonplaceholder.typicode.com",
        changeOrigin: true,
        rewrite: path => path.replace(/^\/api/, '')
      }
    }
  }
})

  • vite-project/src/App.vue
<script setup>
// 发请求做代理
fetch('/api/posts/1').then(res => res.json()).then(data => {
  console.log(data)
})
</script>
<template>
</template>

mock数据

  • 必须在项目根目录的mock目录下。
  1. 安装mock的vite插件

    pnpm i mockjs vite-plugin-mock -D
    
  2. 配置vite插件。

    • vite-project/vite.config.js

      import { viteMockServe } from "vite-plugin-mock";
      export default defineConfig({
        plugins: [
          vue(),
          vueJsx(),
          viteMockServe({})
        ]
      });
      
  3. 定义mock数据

    • vite-project/mock/a.js 必须在项目根目录的mock目录下。

      export default [
          {
              url: '/api/get',
              method: 'get',
              response: () => {
                  // 模拟的json格式
                  return {
                      code: 0,
                      data: { name: 'jw' }
                  };
              },
          }
      ]
      
  4. 调用方法通过开发服务器访问到mock数据。

    • vite-project/src/App.vue

      <script setup>
      // mock数据
      fetch('/api/get').then(res => res.json()).then(data => {
        console.log(data)
      })
      </script>
      <template>
      </template>
      

旧项目的兼容

  • legacy 遗留的产物 vite可以兼容不支持esModule规范的逻辑 (如果不支持就采用动态创建script标签的方式来引入 Systemjs)
pnpm i @vitejs/plugin-legacy -D
  • vite-project/vite.config.js

    import legacy from '@vitejs/plugin-legacy'
    export default defineConfig({
      plugins: [
        vue(),
        vueJsx(),
        legacy() // 在构建过程中生成传统的 ES5 兼容包,以支持旧版本的浏览器
      ],
    })
    

polyfill

  • polyfill 腻子函数,补平一些内置方法。

打包不用esBuild,而是用terser

npm add -D terser
  • vite-project/vite.config.js
export default defineConfig({
  build: {
    minify:'terser', // 使用terser来压缩
    assetsInlineLimit: 200 * 1024
  }, // 支持rollup 的配置
})

UI框架的动态导入

pnpm i @vitejs/plugin-legacy -D
pnpm i unplugin-vue-components -D
pnpm install vant
import Components from 'unplugin-vue-components/vite';
import { VantResolver } from 'unplugin-vue-components/resolvers';

export default defineConfig({
  plugins: [
    Components({
      resolvers: [VantResolver()], // vant组件解析
    })
  ],
});
  1. vant动态导入
pnpm i @vitejs/plugin-legacy -D
pnpm i unplugin-vue-components -D
pnpm install vant
  • vite-project/vite.config.js

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import vueJsx from '@vitejs/plugin-vue-jsx'


import Components from 'unplugin-vue-components/vite';
import { VantResolver } from 'unplugin-vue-components/resolvers';

export default defineConfig({
  // vite的插件直接放在plugis中即可
  plugins: [
    vue(),
    vueJsx(),
    Components({
      resolvers: [VantResolver()], // vant组件解析
    }),
  ],
  // 组件库动态导入 element-plus  vant  andt-design-vue  nativeUI ....
})

  • vite-project/src/App.vue
<script setup>

</script>
<template>
  <van-button>按钮</van-button>
  <van-field />
</template>

Vue3新特性

  • Vue3 迁移指南

组合式api

  • 组合式API
    • 选项式API(method、computed、watch、props、data…)。有一个缺点:一个完整的逻辑会拆分到不同的属性中。高内聚低耦合。管理的时候非常麻烦,反复横跳。
      • 组合式API则将选项式API中的这些api全部转换成函数,将这些函数组合起来完成功能。
      • 组合式api与函数编程:组合式api不是函数式编程 (组合式API是他借鉴过来了函数式编程的思路) 先抄先超。
    • 选项式API:对象里面写的东西打包后就算没用到也会被打包。
      • 组合式api可以做到按需的tree-shakig (整个vue3项目编写后打包出的结果体积小), 以前的公共逻辑在vue2如何处理的 Vue.mixin (解决mixin问题) 高阶组件。
    • 选项式API:options API缺点 this.x this.x this.x ,this满天飞,有时候比较难确定this内部到底有什么东西。

Composition API

  • 使用函数的方式编写vue组件。
  • 组合式API (响应式API ref()、reactive(),生命周期钩子onMounted()、onUnmounted(),依赖注入inject()、provide())
  • 组合式API并不是函数式编程。

组合式API与选项式API

  • 如何看待Composition API 和 Options API?

    • 在Vue2中采用的是OptionsAPI, 用户提供的data,props,methods,computed,watch等属性 (用户编写复杂业务逻辑会出现反复横跳问题)
    • Vue2中所有的属性都是通过this访问,this存在指向明确问题
    • Vue2中很多未使用方法或属性依旧会被打包,并且所有全局API都在Vue对象上公开。Composition API对 tree-shaking 更加友好,代码也更容易压缩。
    • 组件逻辑共享问题, Vue2 采用mixins 实现组件之间的逻辑共享; 但是会有数据来源不明确,命名冲突等问题。 Vue3采用CompositionAPI 提取公共逻辑非常方便
  • image-20230616151243860

将同一个逻辑的相关代码收集在一起,并且可复用。

setup组件

  • 单文件组件中的组合式 API 语法糖 (

Teleport组件

  • 传送门, 在vue2中编写弹框组件,我们不能将弹框渲染到id="app"中。涉及到截断的问题如父元素设置了overflow:hidden, 怎么逃离id="app"的容器?

  • Vue.extend().mount(‘…’)

  • 参考react Portial这个传送门组件,指定将组件渲染到某个容器中。

    • 经常用于处理弹窗组件和模态框组件。
  • vite-project/src/components/teleport.vue

<template>
  <!-- 编写弹框组件比较合适, 但是一般场景用不到 this.$dialog()  this.$toast()  服务调用-->
  <button @click="changeDisplay">显示弹框</button>
  <Teleport to="#root" v-if="isShow">
    这是一个他框组件  
    <button @click="changeDisplay">关闭按钮</button>
  </Teleport>  
</template>


<script>
export default {
  data() {
    return {isShow:false}
  },
  methods: {
    changeDisplay(){this.isShow = !this.isShow}
  }
}
</script>

Fragments组件

  • Fragment(片段)Vue3中允许组件中包含多个节点。无需引入额外的DOM元素。
    • vue2中如果想抽离组件 需要给组件增添一个包裹元素, vue3 则不需要这个无意义的标签了,可以直接写,根节点可以是文本节点。

emits属性

  • vue2挂载原始的事件给组件 需要通过.native修饰来做处理。
  • vue3 默认绑定的事件会被绑定到根元素上, 需要通过emits属性来识别,哪些是自定义的事件。
  • 即通过在子组件的emits属性可将来自于父组件的订阅事件从attrs中移除。
  • 示例
    • vite-project/src/App.vue 父组件

      <template>
        <Emit @click="handleClick"></Emit>
      </template>
      
      <script>
      import Emit from "./components/emit.vue"
      export default {
        componets: {
          Emit
        },
        methods: {
          handleClick(type) {
            alert(type)
          }
        }
      }
      </script>
      
    • vite-project/src/components/emit.vue 子组件

      <template>
          <div @click="handleClick">我是一个按钮</div>
      </template>
      
      <script>
      export default {
          emits:['click'], // 标识哪些事件是自定义的
          methods: {
              handleClick() {
                  this.$emit('click','hello')
              }
          }
      }
      </script>
      

进阶参考

  1. Vue2的diff算法–updateChildren图文流程以及缺点
  2. Vue3 迁移指南

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/754982.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Android 多边形导圆角(Path画折线导圆角)

前言&#xff1a;用path画折线&#xff0c;2条线相交处导圆角 简介&#xff1a;为开发者提供工具类&#xff0c;方便对使用path画折线设置圆角。使用path画多边形&#xff0c;如三角形、矩形、平行四边形等&#xff0c;同时设置圆角。另外提供计算直线上某点坐标。 实现原理 …

Matplotlib下载和安装

Matplotlib 是 Python 的第三方绘图库&#xff0c;它非常类似于 MATLAB。在使用 Matplotlib 软件包之前&#xff0c;需要对其进行安装。本节以 Windows10 系统为例&#xff0c;介绍 Matplotlib 的几种安装方式。 MATLAB 是一款商业软件&#xff0c;主要用于数据分析、图像处理…

http协议(二)

欢迎来到南方有乔木的博客&#xff01;&#xff01;&#xff01; 博主主页&#xff1a;点击点击&#xff01;戳一戳&#xff01;&#xff01; 博主名:南方有乔木呀 博主简介&#xff1a; 一名在校大学生&#xff0c;正在努力学习Java语言编程。穷且意坚&#xff0c;不坠青云…

Java常规写法与新特性对比详解

Java常规写法与新特性对比详解 ✨1. Lambda表达式对比常规写法&#xff1a;⭐️1.1 迭代集合&#xff1a;⭐️1.2 条件过滤&#xff1a;⭐️1.3 映射转换&#xff1a; ✨2. Stream API对比常规写法&#xff1a;⭐️2.1 过滤和计数&#xff1a;⭐️2.2 排序&#xff1a;⭐️2.3 …

通过 Postman+Newman+Jenkins 进行接口自动化测试和进一步实现 CI

目录 前言&#xff1a; Postman 部分 Collection 或者说测试集的建立 Postman 使用详解 测试工具 mock server Newman 的安装和使用 newman 的使用 对 SSL 的支持 newman 第三版以后&#xff08;目前已经是 4.3.1&#xff09;可以支持通过 CLI 选项来支持 SSL 对 new…

Python 使用 Stable Diffusion API 生成图片示例

代码&#xff1a; import base64 import datetime import json import osimport requestsdef submit_post(url: str, data: dict):"""Submit a POST request to the given URL with the given data.:param url: url:param data: data:return: response"…

用html+javascript打造公文一键排版系统6:三级标题排版

正文中的标题分为四级&#xff0c;文中结构层次序数依次可以用“一、”“&#xff08;一&#xff09;”“1.”“&#xff08;1&#xff09;”标注&#xff1b;一般第一层用黑体字、第二层用楷体字加粗、第三层和第四层用仿宋体字标注。 对于以阿拉伯数字开头、后接英文点号.及…

First image then video A two-stage network for spatiotemporal video denoising

First image then video: A two-stage network for spatiotemporal video denoising http://export.arxiv.org/abs/2001.00346 作者&#xff1a;王策 南开大学 本文针对的是视频中出现大运动。或者前景和背景由于弱光环境较弱时造成去噪性能差的问题而解决&#xff0c;论文时在…

String 类的常用方法

String类的常用方法 说明 String类是保存字符串常量的&#xff0c;每次更新都需要重新开辟空间&#xff0c;效率比较低&#xff0c;因此 java 设计者还提供了StringBuilder和StringBuffer来增强String的功能&#xff0c;并提高效率。 常用方法 public class String01 {publi…

C语言——动态内存管理(malloc, calloc, realloc, free, 柔性数组详解)

C语言——动态内存管理 1. 为什么需要动态内存管理 我们以往定义数组&#xff0c;都是这么定义的&#xff1a; int nums[10] {0};以这种方式开辟空间有两个特点&#xff1a; 空间开辟的大小是固定的数组在声明的时候&#xff0c;必须指定数组的长度&#xff0c;它所需要的内…

数据库应用:Navicat连接MySQL

目录 一、理论 1.Navicat 2.MVCC 二、实验 1.Navicat连接MySQL 2.navicat的基础操作 3.测试提交事务 三、问题 1.解决1130 2.解决2003 四、总结 一、理论 1.Navicat &#xff08;1&#xff09;简介 Navicat Premium 是一套数据库开发工具&#xff0c;实现从单一应用…

基于时域特征和频域特征组合的敏感特征集,再利用CNN进行轴承故障诊断(python编程)

1.文件夹介绍&#xff08;使用的是CWRU数据集&#xff09; 0HP-3HP四个文件夹装载不同工况下的内圈故障、外圈故障、滚动体故障和正常轴承数据。 2.模型 按照1024的长度分割样本&#xff0c;构建内圈故障、外圈故障、滚动体故障和正常轴承样本集 2.1.计算11种时域特征值 # 计…

EfficientNet论文笔记

EfficientNet论文笔记 通过NAS平衡了channel&#xff0c;depth&#xff0c;resolution&#xff0c;发现在相同的FLOPs下&#xff0c;同时增加 depth和 resolution的效果最好。 数据集效果小于resolution怎么办&#xff1f; EfficientNet—b0框架 表格中每个MBConv后会跟一个…

Maven —— 项目管理工具

前言 在这篇文章中&#xff0c;荔枝会介绍如何在项目工程中借助Maven的力量来开发&#xff0c;主要涉及Maven的下载安装、环境变量的配置、IDEA中的Maven的路径配置和信息修改以及通过Maven来快速构建项目。希望能对需要配置的小伙伴们有帮助哈哈哈哈~~~ 文章目录 前言 一、初…

力扣刷题序列 - 字符串篇

这里写目录标题 字符1.520 回文串的定义2. 125 公共前缀3. 14 单词4. 4345. 58 字符串的反转6. 3447. 5418. 5579. 151 字符的统计10. 38711. 38912. 38313. 24214. 4915. 45116. 42317. 65718. 55119. 69620. 467 数字与字符间的转换21.41222.50623.53924.553537592---64038 子…

GAME101 OpenCV环境安装

文章目录 Opencv 库编译Step 1.下载源码Step 2. 使用CMake编译Step3. 解决CMake 过程重的报错错误1&#xff1a; 错误的Python版本:错误1 解决办法 错误2&#xff1a;下载ippicv_2020_win_ia32_20191018_general.zip失败错误2 解决办法 错误3&#xff1a;ffmpeg相关文件下载失败…

力扣 509.斐波那契数

509.斐波那契数 1 题目2 思路3 代码4 结果 1 题目 题目来源&#xff1a;力扣&#xff08;LeetCode &#xff09;https://leetcode.cn/problems/fibonacci-number 斐波那契数 &#xff08;通常用 F(n) 表示&#xff09;形成的序列称为斐波那契数列 。该数列由 0 和 1 开始&…

git国内下载

https://npm.taobao.org/mirrors/git-for-windows/点进去最新的最后一条 选择.exe文件点击

二、遥感物理基础(2)物体的发射与反射辐射特征

前言 本文内容较为枯燥&#xff0c;是遥感的物理原理&#xff0c;作者已经尽量去帮助读者理解了&#xff0c;无论是精细的阅读还是走马观花&#xff0c;长期下来都能提高读者对专业知识的理解&#xff1b;作者非物理专业&#xff0c;对某些知识点的总结仅是个人理解&#xff0c…

win7 刻录机刻录文件显示 “准备好写入到”光盘中的文件”

一、问题描述 就是这么突然&#xff0c;好好的刻录机&#xff0c;突然就刻录不了了&#xff0c;昨天都可以正常刻录&#xff0c;今天就显示&#xff1a; 准备好写入到”光盘中的文件&#xff0c;然后还显示待刻录的文件…&#xff0c;右键选择刻录就直接弹出光盘了&#xff1f…