vue3 + vite

端口控制 + 跨域

在vite.config.js文件中

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import path from 'path';

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [vue()],
  base: "./",//打包路径
  resolve: {
    alias: {
      '@': path.resolve(__dirname, './src')//设置别名
    }
  },
  server: {
    open: true,//启动项目自动弹出浏览器
    port: 4000,//启动端口
    proxy: {
      '/api': {
        target: 'http://localhost:3001',	//实际请求地址
        changeOrigin: true,
        rewrite: (path) => path.replace(/^\/api/, '')
      },
    }
  }
  // server: {
  //   port: 4000,//启动端口
  //   open: true,
  //   proxy: {
  //     // 跨域简单写法写法
  //     '/api': 'http://123.56.85.24:5000'//代理网址
  //   },
  //   cors: true
  // },
})

拦截器

import Axios from 'axios'
const axios = Axios.create({
  headers: {
    'Content-Type': 'application/json'
  },
  timeout: 60000, // 超时
  baseURL: '' // 请求接口地址,这里使用本项目地址,因为我们是前后端分离,后面需要在vue.config.js里面配置代理,实际请求得地址不是这个。
})

// 请求拦截
axios.interceptors.request.use(req => {
  if (req.method === 'get') {
    const url = req.url
    const t = new Date().getTime()
    if (url.indexOf('?') >= 0) {
      req.url = `${url}&t=${t}`
    } else {
      req.url = `${url}?t=${t}`
    }
  }
  return req
})

// 响应拦截
axios.interceptors.response.use(
  response => {
    return response.data
  },
  error => {
    // 响应失败统一处理
    const { response } = error
    if (response) {
      switch (response.status) {
        case 400:
          window.$vm.$message.error('请求无效')
          break
        case 401:
          window.$vm.$message.error({ message: '尚未登录请重新登录' })
          break
        case 403:
          window.$vm.$message.error('您没有权限这样做,请联系管理员')
          break
        case 404:
          window.$vm.$message.error('请求未找到')
          break
        case 500:
          window.$vm.$message.error('系统异常')
          break
        case 504:
          window.$vm.$message.error('请求超时,请稍后再试')
          break
        default:
          window.$vm.$message.error('系统异常')
          break
      }
    }
    return Promise.reject(error)
  }
)
export default axios

api

import request from '../utils/request' // 引入封装得axios

// 登录
export function cellphone (data) {
  return request({
    url: '/api/login/cellphone',
    method: 'POST',
    data:{
      phone:'19829215473',
      password:'lsh112233'
    }
  })
}

Element plus 引入

  1. npm install element-plus --save

  2. 按需导入(自动导入):
    npm install -D unplugin-vue-components unplugin-auto-import

  3. 在vite.config.js 或 vite.config.ts中写入

import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'

export default {
  plugins: [
    // ...
    AutoImport({
      resolvers: [ElementPlusResolver()],
    }),
    Components({
      resolvers: [ElementPlusResolver()],
    }),
  ],
}

ref

import {ref,isRef,shallowRef,triggerRef,customRef} from "vue";
/**
 * ref()   响应式
 * isRef()    判断是否为ref
 * shallowRef()    浅层响应式    注意:与ref混用时,失效
 * triggerRef()    强制收集依赖,shallowRef与ref混用时失效就是由于,ref底层代码中包含triggerRef
 * customRef()    自定义一个ref
 *                  例:function MyRef(value: T) {
                          return customRef((track, trigger) => {
                            return {
                              get() {
                                track();
                                return value;
                              },
                              set(newValue) {
                                value = newValue;
                                trigger();
                              },
                            };
                          });
                        }

                        const obj = ref("大家好");
 */

直接使用ref

		
哈哈哈
const dom = ref(); console.log(dom.value?.innerHTML); // 注意:刚开始加载时间听不到的

reactive






toRef toRefs toRaw








computed








watch








watchEffect – 高级侦听器

立即执行传入的一个函数,并响应式追踪其依赖,并在其依赖变更时重新运行该函数。

用法:
import { watchEffect } from ‘vue’

setup()
{
	
    watchEffect(函数(){
    	依赖追踪:
    		同步调用:
    			追踪所有依赖
    			
    		异步调用:
    			只有在第一个 await 正常工作前访问到的 property 才会被追踪。
    			
    			如下:只会追踪url.value作为依赖
    			const response = await fetch(url.value)
				data.value = await response.json()
    })	
   
   	(1)停止侦听
   	
   	  隐式停止:
   		当watchEffect在组件的setup()函数或生命周期钩子被调用时,侦听器会被链接到该组件的生命周期,并在组件卸载时自动停止。
   		
   	  显示停止:
   	  	const xx=watchEffect(...)
   	  	xx.stop();
   	  	
   	(2)清除副作用
   		watchEffect传入参数
   		
		watchEffect((onInvalidate)=>{

	      onInvalidate(()=>{
		      执行时机:
				在副作用即将重新执行时
				如果在setup()或生命周期钩子函数中使用了 watchEffect, 则在卸载组件时
	      })
	
	    })
	    
	(3)异步副作用以及清楚
		副作用函数是一个异步函数时,清理函数必须要在Promise被resolve之前被注册,同时Vue依赖这个返回的Promise来自动处理Promise链上的潜在错误
		watchEffect(async () => {
			onInvalidate(() => {
			    /* ... */
			})
			data.value = await fetchData(props.id)
		})
	
	(4)副作用需要同步或在组件更新之前
		watchEffect(
		  () => {
				...
		  },
		  {
		    flush: 'sync',    'pre'在DOM更新前运行,'post'在DOM更新后运行,'sync'强制效果始终同步触发,默认为'pre'
		  }
		)
		
	(5)调试侦听器的行为(只能在开发模式下工作)
		watchEffect(
		  () => {
		    ...
		  },
		  {
		    onTrack(e){
		    	当一个 reactive对象属性或一个 ref 作为依赖被追踪时触发
		    	e.target包含了值
		    	debugger
		    },
		    onTrigger(e) {
		        依赖项变更导致副作用被触发时
		        e.target包含了值
		        debugger
		    }
		  }
		)

(2)watchPostEffect
和flush:‘post’ 效果一致,作为别名使用
(3)watchSyncEffect
和flush:‘sync’ 效果一致,作为别名使用

(4)卸载watchEffect时机
异步回调创建一个侦听器,那么它不会绑定到当前组件上,必须手动停止它,以防内存泄漏,同步则不用

需要手动卸载:
setTimeout(() => {
  watchEffect(() => {})
}, 100)

生命周期

注:当作为组件引入时,大体与vue2一致,但不需要在computed中声明








父传子

父组件

	






子组件








子传父

父组件








子组件

	






子组件向父组件暴露一些属性

子组件

	





父组件








局部组件

  1. 在components中书写.vue文件
  2. 在对应页面引入



全局组件

  1. 在components中书写.vue文件
  2. 在mine.ts中声明
import { createApp } from 'vue'
import './style.css'
import HelloWorldVue from './components/HelloWorld.vue'  // 在main.ts中引入
import App from './App.vue'

const app = createApp(App)
app.component('A', HelloWorldVue)  // 声明

app.mount('#app')
  1. 在对应页面引入

可选链操作符

例:
	let a = {};
	console.log(a.name);   // underfind
	console.log(a.name.length);   // 报错

	console.log(a.name?.length);   // underfind      可选链操作符:如果后面的一层读不到的话会反一个underfind

动态组件

  1. 方法一






  1. 方法2









slot 插槽

匿名插槽

// 父组件




// 子组件

具名插槽

// 父组件




// 子组件

作用域插槽

// 父组件





// 子组件




异步组件(代码优化)

顶层 await
在setup语法糖里面 使用方法
< script setup > 中可以使用顶层 await。结果代码会被编译成 async setup()

	const { data } = await axios.get("./data.json");

父组件引用子组件 通过defineAsyncComponent加载异步配合import 函数模式便可以分包





Teleport传送组件

Teleport Vue 3.0新特性之一。

Teleport 是一种能够将我们的模板渲染至指定DOM节点,不受父级style、v-show等属性影响,但data、prop数据依旧能够共用的技术;类似于 React 的 Portal。

主要解决的问题 因为Teleport节点挂载在其他指定的DOM节点下,完全不受父级style样式影响

使用方法
通过to 属性 插入指定元素位置 to=“body” 便可以将Teleport 内容传送到指定位置


    

也可以自定义传送位置 支持 class id等 选择器

    

 


多个使用场景

	
     


     

动态控制teleport
使用disabled 设置为 true则 to属性不生效 false 则生效

    
      
    

内置组件keep-alive

有时候我们不希望组件被重新渲染影响使用体验;或者处于性能考虑,避免多次重复渲染降低性能。而是希望组件可以缓存下来,维持当前的状态。这时候就需要用到keep-alive组件。

开启keep-alive 生命周期的变化

初次进入时: onMounted> onActivated
退出后触发 deactivated
再次进入:
只会触发 onActivated
事件挂载的方法等,只执行一次的放在 onMounted中;组件每次进去执行的方法放在 onActivated中



  

 


  
  

 


  
    
  

include 和 exclude


include 和 exclude 允许组件有条件地缓存。二者都可以用逗号分隔字符串、正则表达式或一个数组来表示:

max


  

Transition

  1. 过度单个元素







Transition自定义类名








transtions结合Animate.css

  1. 安装:npm install animate.css -S



Transtion的duration属性





Transtion声明周期








Transtion 结合动画库jsap使用




Transition 的 appear属性






Transition-group过度列表

  1. 默认情况下,它不会渲染一个包裹元素,但是你可以通过 tag attribute 指定渲染一个元素。
  2. 过渡模式不可用,因为我们不再相互切换特有的元素。
  3. 内部元素总是需要提供唯一的 key attribute 值。
  4. CSS 过渡的类将会应用在内部的元素中,而不是这个组/容器本身。

     
{{ item }
const list = reactive([1, 2, 4, 5, 6, 7, 8, 9])
const Push = () => {
    list.push(123)
}
const Pop = () => {
    list.pop()
}

< transition-group > 组件还有一个特殊之处。除了进入和离开,它还可以为定位的改变添加动画。只需了解新增的 v-move 类就可以使用这个新功能,它会应用在元素改变定位的过程中。像之前的类名一样,它的前缀可以通过 name attribute 来自定义,也可以通过 move-class attribute 手动设置


  

  






Vue 也同样可以给数字 Svg 背景颜色等添加过度动画 今天演示数字变化


    

    

依赖注入Provide / Inject

provide 可以在祖先组件中指定我们想要提供给后代组件的数据或方法,而在任何后代组件中,我们都可以使用 inject 来接收 provide 提供的数据或方法。

  1. 祖先组件(声明provide)







  1. 后代组件(调用inject)














后代修改祖先传的值

可以添加readonly使后代组件无法修改祖先传的值:provide(“color”, readonly(color));

  1. 祖先组件(声明provide)







  1. 后代组件







兄弟组件传参

方法一:借用父组件中转

父组件








传参组件








接参组件








方法二:使用Event Bus

原码实现:

bus.ts

type BusClass = {
  emit: (name: string) => void
  on: (name: string, callback: Function) => void
}
type PramsKey = string | number | symbol

type List = {
  [key: PramsKey]: Array
}

class Bus implements BusClass {
  list: List
  constructor() {
    this.list = {}
  }
  emit(name: string, ...args: Array) {
    let eventName: Array = this.list[name]
    eventName.forEach(fn => {
      fn.apply(this, args)
    })
  }

  on(name: string, callback: Function) {
    // 如果之前有就直接用,如果没有就创建一个空的数组
    let fn: Array = this.list[name] || []
    fn.push(callback)
    this.list[name] = fn
  }
}

export default new Bus()

父组件








兄弟组件















方法二:Mitt(对于event Bus的封装)

在vue3中 o n , on, onoff 和 $once 实例方法已被移除,组件实例不再实现事件触发接口,因此大家熟悉的EventBus便无法使用了。然而我们习惯了使用EventBus,对于这种情况我们可以使用Mitt库(其实就是我们视频中讲的发布订阅模式的设计)

1.安装

npm install mitt -S

2.main.ts 初始化
全局总线,vue 入口文件 main.js 中挂载全局属性

import { createApp } from 'vue'
import App from './App.vue'
import mitt from 'mitt'
 
const Mit = mitt()
 
//TypeScript注册
// 由于必须要拓展ComponentCustomProperties类型才能获得类型提示
declare module "vue" {
    export interface ComponentCustomProperties {
        $Bus: typeof Mit
    }
}
 
const app = createApp(App)
 
//Vue3挂载全局API
app.config.globalProperties.$Bus = Mit
 
app.mount('#app')

3使用方法通过emit派发, on 方法添加事件,off 方法移除,clear 清空所有

A组件派发(emit)


 

 

B组件监听(on)


 

 

监听所有事件( on(“*”) )

instance?.proxy?.$Bus.on('*',(type,num)=>{
    console.log(type,num,'===========>B')
})

移除监听事件(off)

const Fn = (num: any) => {
    console.log(num, '===========>B')
}
instance?.proxy?.$Bus.on('on-num',Fn)//listen
instance?.proxy?.$Bus.off('on-num',Fn)//unListen

清空所有监听(clear)

instance?.proxy?.$Bus.all.clear() 

TSX

  1. 安装 (装不上,下来找原因)
npm install @vitejs/plugin-vue-jsx -D

自动引入插件

  1. 安装
npm i -D unplugin-auto-import
  1. 配置 vite.config.ts文件
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'

import { resolve } from 'path'

import AutoImport from 'unplugin-auto-import/vite'   // 引入

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [vue(),
  // 声明
  AutoImport({
    imports: ['vue'],  // 语言
    dts: 'src/auto-import.d.ts'   // 配置信息位置 
  })],
  resolve: {
    alias: {
      "@": resolve(__dirname, 'src'), // 路径别名
    },
    extensions: ['.js', '.json', '.ts'] // 使用路径别名时想要省略的后缀名,可以自己 增减
  }
})

演示





v-model

其实是一个语法糖,由props与emit组合而成

1.默认值的改变

prop:value -> modelValue;
事件:input -> update:modelValue;
v-bind 的 .sync 修饰符和组件的 model 选项已移除
新增 支持多个v-model
新增 支持自定义 修饰符 Modifiers

单v-model

父子组件传值















多个v-model















自定义修饰符

添加到组件 v-model 的修饰符将通过 modelModifiers prop 提供给组件。在下面的示例中,我们创建了一个组件,其中包含默认为空对象的 modelModifiers prop


函数简写

只在 mounted 和 updated 时触发


   

练习

思路:监听鼠标按下的事件,当鼠标按下后监听鼠标移动,并动态改变元素位置,最后鼠标松开关闭事件








自定义的hook

Vue3 的 hook函数 相当于 vue2 的 mixin, 不同在与 hooks 是函数
Vue3 的 hook函数 可以帮助我们提高代码的复用性, 让我们能在不同的组件中都利用 hooks 函数
Vue3 hook 库Get Started | VueUse : https://vueuse.org/guide/

案例

import { onMounted } from 'vue'
 
 
type Options = {
    el: string
}
 
type Return = {
    Baseurl: string | null
}
export default function (option: Options): Promise {
 
    return new Promise((resolve) => {
        onMounted(() => {
            const file: HTMLImageElement = document.querySelector(option.el) as HTMLImageElement;
            file.onload = ():void => {
                resolve({
                    Baseurl: toBase64(file)
                })
            }
 
        })
 
 
        const toBase64 = (el: HTMLImageElement): string => {
            const canvas: HTMLCanvasElement = document.createElement('canvas')
            const ctx = canvas.getContext('2d') as CanvasRenderingContext2D
            canvas.width = el.width
            canvas.height = el.height
            ctx.drawImage(el, 0, 0, canvas.width,canvas.height)
            console.log(el.width);
            
            return canvas.toDataURL('image/png')
 
        }
    })
 
 
}

自定义全局函数和变量

由于Vue3 没有Prototype 属性 使用 app.config.globalProperties 代替 然后去定义变量和函数

在 mine.ts下写入,如:

app.config.globalProperties.$env = '123'
app.config.globalProperties.$env2 = {
  name(str: T) {
    return '我叫' + str
  }
}


type Filter = {
  name(str: T): string
}

// 声明要扩充@vue/runtime-core包的声明.
// 这里扩充"ComponentCustomProperties"接口, 因为他是vue3中实例的属性的类型.
declare module 'vue' {
  export interface ComponentCustomProperties {
    $env: string
    $env2: Filter
  }
}

在setup中使用

方式一:
import { getCurrentInstance, ComponentInternalInstance } from 'vue';
const { appContext } = getCurrentInstance()
console.log(appContext.config.globalProperties.$env);
 
推荐 方式二:
 
import {ref,reactive,getCurrentInstance} from 'vue'
const app = getCurrentInstance()
console.log(app?.proxy?.$set2.name('李四'))

自定义插件

插件
插件是自包含的代码,通常向 Vue 添加全局级功能。你如果是一个对象需要有install方法Vue会帮你自动注入到install 方法 你如果是function 就直接当install 方法去使用

使用插件
在使用 createApp() 初始化 Vue 应用程序后,你可以通过调用 use() 方法将插件添加到你的应用程序中。

实现一个Loading插件

components/Loading.vue


    

components/Loading.ts

import {  createVNode, render, VNode, App } from 'vue';
import Loading from './index.vue'
 
export default {
    install(app: App) {
        //createVNode vue提供的底层方法 可以给我们组件创建一个虚拟DOM 也就是Vnode
        const vnode: VNode = createVNode(Loading)
        //render 把我们的Vnode 生成真实DOM 并且挂载到指定节点
        render(vnode, document.body)
        // Vue 提供的全局配置 可以自定义
        app.config.globalProperties.$loading = {
            show: () => vnode.component?.exposed?.show(),
            hide: () => vnode.component?.exposed?.hide()
        }
 
    }
}

main.ts

import Loading from './components/loading'
 
 
let app = createApp(App)
 
app.use(Loading)
 
 
type Lod = {
    show: () => void,
    hide: () => void
}
//编写ts loading 声明文件放置报错 和 智能提示
declare module '@vue/runtime-core' {
    export interface ComponentCustomProperties {
        $loading: Lod
    }
}
 
 
app.mount('#app')

使用


 


样式穿透 /deep/ 和 :deep

vue3推荐:deep

插槽选择器

A 组件定义一个插槽



在App.vue 引入


 

全局选择器


css Style完整新特性

动态 CSS

单文件组件的


 

 

如果是对象 v-bind 请加引号

 
 

 

4.css module

自定义注入名称(多个可以用数组)

你可以通过给 module attribute 一个值来自定义注入的类对象的 property 键


 

与组合式 API 一同使用

注入的类可以通过 useCssModule API 在 setup() 和


 
 

 

使用场景一般用于TSX 和 render 函数 居多

Vue3集成Tailwind CSS

Tailwind CSS中文官网地址:https://www.tailwindcss.cn/
安装学习地址:http://t.csdn.cn/jrQvZ

Event Loop

JS 执行机制
在我们学js 的时候都知道js 是单线程的如果是多线程的话会引发一个问题在同一时间同时操作DOM 一个增加一个删除JS就不知道到底要干嘛了,所以这个语言是单线程的但是随着HTML5到来js也支持了多线程webWorker 但是也是不允许操作DOM

单线程就意味着所有的任务都需要排队,后面的任务需要等前面的任务执行完才能执行,如果前面的任务耗时过长,后面的任务就需要一直等,一些从用户角度上不需要等待的任务就会一直等待,这个从体验角度上来讲是不可接受的,所以JS中就出现了异步的概念。

同步任务
代码从上到下按顺序执行

异步任务
1.宏任务
script(整体代码)、setTimeout、setInterval、UI交互事件、postMessage、Ajax

2.微任务
Promise.then catch finally、MutaionObserver、process.nextTick(Node.js 环境)

运行机制

所有的同步任务都是在主进程执行的形成一个执行栈,主线程之外,还存在一个"任务队列",异步任务执行队列中先执行宏任务,然后清空当次宏任务中的所有微任务,然后进行下一个tick如此形成循环。

next

nextTick 就是创建一个异步任务,那么它自然要等到同步任务执行完成后才执行。


   

unocss原子化

参考:http://t.csdn.cn/AkrHY

函数式编程,h函数

参考:http://t.csdn.cn/tD9Nc

Vue开发桌面程序Electron

ps:打包后白屏,无法解决。爆炸…
开发桌面程序

创建一个vite 项目
npm init vite@latest

安装electron
npm install electron -D
npm install vite-plugin-electron -D

根目录新建 electron / index.ts

修改vite.config.ts 配置文件
引入electron插件配置main entry对应electron的文件

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

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [vue(), electron({
    // 这里需要调整main对象,vite-plugin-electron 0.10.4中使用main会报错。
    // main: {
    //   entry: "electron/index.ts"
    // }
    entry: "electron/index.ts"
  })],
})

编写代码 electron / index.ts

import { app, BrowserWindow } from 'electron'
import path from 'path'
//app 控制应用程序的事件生命周期。
//BrowserWindow 创建并控制浏览器窗口。
 
let win: BrowserWindow | null;
//定义全局变量获取 窗口实例
 
const createWindow = () => {
    win = new BrowserWindow({
        //
        webPreferences: {
            devTools: true,
            contextIsolation: false,
            nodeIntegration: true
            //允许html页面上的javascipt代码访问nodejs 环境api代码的能力(与node集成的意思)
        }
    })
    if (app.isPackaged) {
        win.loadFile(path.join(__dirname, "../index.html"));
    } else {
//VITE_DEV_SERVER_HOST 如果是undefined 换成  VITE_DEV_SERVER_HOSTNAME
       win.loadURL(`${process.env['VITE_DEV_SERVER_URL']}`);
    }
}
//isPackage 不好使换下面的
  //  if(process.env.NODE_ENV != 'development'){
      //  win.loadFile(path.join(__dirname, "../index.html"));
  //  }else{
        //win.loadURL(`${process.env['VITE_DEV_SERVER_URL']}`);}`)
   // }
//在Electron完成初始化时被触发
app.whenReady().then(createWindow)

配置package json 增加main 字段 type 去掉

{
  "name": "my_vite",
  "private": true,
  "version": "0.0.0",
  "main": "dist-electron/index.js",
  "scripts": {
    "dev": "vite",
    "build": "vue-tsc --noEmit && vite build && electron-builder",
    "preview": "vite preview"
  },
  "dependencies": {
    "cross-env": "^7.0.3",
    "vue": "^3.2.45"
  },
  "devDependencies": {
    "@vitejs/plugin-vue": "^4.0.0",
    "electron": "^22.0.0",
    "electron-builder": "^23.6.0",
    "typescript": "^4.9.3",
    "vite": "^4.0.0",
    "vite-plugin-electron": "^0.10.4",
    "vue-tsc": "^1.0.11"
  },
  "build": {
    "appId": "com.electron.desktop",
    "productName": "electron",
    "asar": true,
    "copyright": "Copyright © 2022 electron",
    "directories": {
      "output": "release/"
    },
    "files": [
      "dist-electron"
    ],
    "mac": {
      "artifactName": "${productName}_${version}.${ext}",
      "target": [
        "dmg"
      ]
    },
    "win": {
      "target": [
        {
          "target": "nsis",
          "arch": [
            "x64"
          ]
        }
      ],
      "artifactName": "${productName}_${version}.${ext}"
    },
    "nsis": {
      "oneClick": false,
      "perMachine": false,
      "allowToChangeInstallationDirectory": true,
      "deleteAppDataOnUninstall": false
    },
    "publish": [
      {
        "provider": "generic",
        "url": "http://127.0.0.1:8080"
      }
    ],
    "releaseInfo": {
      "releaseNotes": "版本更新的具体内容"
    }
  }
}

npm run dev

打包Electron

需要安装electron-builder

npm install electron-builder -D

package json 配置 build 修改npm run build命令

"build": "vue-tsc --noEmit && vite build  &&  electron-builder",
  "build": {
    "appId": "com.electron.desktop",
    "productName": "electron",
    "asar": true,
    "copyright": "Copyright © 2022 electron",
    "directories": {
      "output": "release/"
    },
    "files": [
      "dist"
    ],
    "mac": {
      "artifactName": "${productName}_${version}.${ext}",
      "target": [
        "dmg"
      ]
    },
    "win": {
      "target": [
        {
          "target": "nsis",
          "arch": [
            "x64"
          ]
        }
      ],
      "artifactName": "${productName}_${version}.${ext}"
    },
    "nsis": {
      "oneClick": false,
      "perMachine": false,
      "allowToChangeInstallationDirectory": true,
      "deleteAppDataOnUninstall": false
    },
    "publish": [
      {
        "provider": "generic",
        "url": "http://127.0.0.1:8080"
      }
    ],
    "releaseInfo": {
      "releaseNotes": "版本更新的具体内容"
    }
  }
npm run build

Vue响应性语法糖

注:将来可能有用
参考:http://t.csdn.cn/EP0wW

环境变量

  1. 查看项目的环境变量
// 查看环境变量
console.log(import.meta.env);

/**
 * BASE_URL: "/"     路由应用前缀
 *
 * DEV: true    // 是否开发环境
 *
 * MODE: "development"     // 当前运行环境
 *
 * PROD: false     // 生否生产环境
 *
 * SSR: false      // 是否服务端渲染
 */
  1. 配置开发环境:
    在根目录下创建.env.development文件
    注意:.env后可以是任意名称,只是通常情况下都叫development
// 在.env.development里写入
VITE_HTTP=http://www.baidu.com
// 在package.json的dev里添加:
// --mode 配置开发环境的文件名
// 现在已经默认读取了
  "scripts": {
    "dev": "vite  --mode development",
    "build": "vue-tsc && vite build",
    "preview": "vite preview"
  },
  1. 配置生产环境:
    在根目录下创建.env.production文件
    注意:.env后可以是任意名称,只是通常情况下都叫production
// 在.env.development里写入
VITE_HTTP=http://www.jd.com

// package.json默认读取production。不需要配置

  1. 查看打包文件
    打开dist文件夹:
安装:npm i http-serve -g
输入:http-server -p 9002
  1. 在vite中获取环境变量
// 引入
import { defineConfig, loadEnv } from 'vite'
import vue from '@vitejs/plugin-vue'

// https://vitejs.dev/config/
export default ({ mode }: any) => {
  console.log(loadEnv(mode, process.cwd));

  return defineConfig({
    plugins: [vue()],
  })
} 

webpack 构建 Vue3项目

为什么要手写webpack 不用cli (脑子有病)并不是 其实是为了加深我们对webpack 的了解方便以后灵活运用webpack 的技术

参考:http://t.csdn.cn/Alf9Q

Vue3 性能优化

性能优化

我们可以使用谷歌浏览器自带的DevTools 进行性能分析 LightHouse

参数介绍

选项代表意思:
FCP (First Contentful Paint):首次内容绘制的时间,浏览器第一次绘制DOM相关的内容,也是用户第一次看到页面内容的时间。
Speed Index: 页面各个可见部分的显示平均时间,当我们的页面上存在轮播图或者需要从后端获取内容加载时,这个数据会被影响到。
LCP (Largest Contentful Paint):最大内容绘制时间,页面最大的元素绘制完成的时间。
TTI(Time to Interactive):从页面开始渲染到用户可以与页面进行交互的时间,内容必须渲染完毕,交互元素绑定的事件已经注册完成。
TBT(Total Blocking Time):记录了首次内容绘制到用户可交互之间的时间,这段时间内,主进程被阻塞,会阻碍用户的交互,页面点击无反应。 CLS(Cumulative Layout Shift):计算布局偏移值得分,会比较两次渲染帧的内容偏移情况,可能导致用户想点击A按钮,但下一帧中,A按钮被挤到旁边,导致用户实际点击了B按钮。

代码分析

由于我们使用的是vite vite打包是基于rollup 的我们可以使用 rollup 的插件

npm install rollup-plugin-visualizer

vite.config.ts 配置 记得设置open 不然无效

import { visualizer } from 'rollup-plugin-visualizer';
plugins: [vue(), vueJsx(),visualizer({
      open:true
 })],

然后进行npm run build打包

Vite 配置优化

build:{
       chunkSizeWarningLimit:2000,
       cssCodeSplit:true, //css 拆分
       sourcemap:false, //不生成sourcemap
       minify:false, //是否禁用最小化混淆,esbuild打包速度最快,terser打包体积最小。
       assetsInlineLimit:5000 //小于该值 图片将打包成Base64 
},

PWA离线存储技术

npm install vite-plugin-pwa -D

修改vite.config.ts

import { VitePWA } from 'vite-plugin-pwa' 
plugins: [vue(),VitePWA(
	      workbox:{
          cacheId:"XIaoman",//缓存名称
          runtimeCaching:[
            {
              urlPattern:/.*\.js.*/, //缓存文件
              handler:"StaleWhileRevalidate", //重新验证时失效
              options:{
                cacheName:"XiaoMan-js", //缓存js,名称
                expiration:{
                  maxEntries:30, //缓存文件数量 LRU算法
                  maxAgeSeconds:30 * 24 * 60 * 60 //缓存有效期
 
                }
              }
            }
          ]
      },
)],

PWA 技术的出现就是让web网页无限接近于Native 应用

可以添加到主屏幕,利用manifest实现
可以实现离线缓存,利用service worker实现
可以发送通知,利用service worker实现

进行 npm run build 打包会生成 sw.js

其他性能优化

  1. 图片懒加载
import lazyPlugin from 'vue3-lazy'


  1. 虚拟列表
    当后台一次返回大量列表数据时,可以使用虚拟列表。
    其实际原理是,在可视化区域展示Dom
    element-plus已集成
  2. 多线程 使用 new Worker 创建

worker脚本与主进程的脚本必须遵守同源限制。他们所在的路径协议、域名、端口号三者需要相同

const myWorker1 = new Worker("./calcBox.js");

都使用postMessage发送消息

worker.postMessage(arrayBuffer, [arrayBuffer]);

都使用onmessage接收消息

self.onmessage = function (e) {
 // xxx这里是worker脚本的内容
};

关闭

worker.terminate();    

VueUse 库已经集成了 webWorker

  1. 防抖节流

同样VueUse 也是集成了

Vue3 Web Components

Web Components 提供了基于原生支持的、对视图层的封装能力,可以让单个组件相关的 javaScript、css、html模板运行在以html标签为界限的局部环境中,不会影响到全局,组件间也不会相互影响 。 再简单来说:就是提供了我们自定义标签的能力,并且提供了标签内完整的生命周期 。

参考:https://blog.csdn.net/qq1195566313/article/details/127328300

你可能感兴趣的:(VUE,前端,uniapp)