本文已参与「新人创作礼」活动,一起开启掘金创作之路。
一、watch监听及深度监听
watch:{
a(val, oldVal){//普通的watch监听
console.log("a: "+val, oldVal);
},
b:{//深度监听,可监听到对象、数组的变化
handler(val, oldVal){
console.log("b.c: "+val.c, oldVal.c);
},
deep:true //true 深度监听
}
}
二、directive自定义指令详解+实例
说明
除了核心功能默认内置的指令 (v-model 和 v-show),Vue 也允许注册自定义指令。注意,在 Vue2.0 中,代码复用和抽象的主要形式是组件。然而,有的情况下,你仍然需要对普通 DOM 元素进行底层操作,这时候就会用到自定义指令。
实例1
拖拽:
Drag.js:
export default function(el){
let oDiv=el;
oDiv.onmousedown=function(e){
let l=e.clientX-oDiv.offsetLeft;
let t=e.clientY-oDiv.offsetTop;
document.onmousemove=function(e){
oDiv.style.left=e.clientX-l+'px';
oDiv.style.top=e.clientY-t+'px';
};
oDiv.onmouseup=function(){
document.onmousemove=null;
oDiv.onmouseup=null;
}
}
}
Vue引用:
<div v-drag>我可以拖拽</div>
import drag from 'drag.js'
Vue.directive('drag',drag)
实例2
当页面加载时,该元素将获得焦点 (注意:autofocus 在移动版 Safari 上不工作)。事实上,只要你在打开这个页面后还没点击过任何内容,这个输入框就应当还是处于聚焦状态。现在让我们用指令来实现这个功能:
// 注册一个全局自定义指令 `v-focus`
Vue.directive('focus', {
// 当被绑定的元素插入到 DOM 中时……
inserted: function (el) {
// 聚焦元素
el.focus()
}
})
如果想注册局部指令,组件中也接受一个 directives 的选项:
directives: {
focus: {
// 指令的定义
inserted: function (el) {
el.focus()
}
}
}
然后你可以在模板中任何元素上使用新的 v-focus property,如下:
<input v-focus>
三、vue父子组件中数据双向绑定的几种方法
一、.sync(支持多个双向绑定值)
大致说明:
- 绑定值加上 .sync
- $emit 去触发 update:prop名,实现修改父组件的变量值实现双向数据流
- :startDate.sync="startDate" 等价于 :startDate="startDate" @updata:startDate="startDate= event就是子组件$emit传递的参数)
例:
父组件
<template>
<div>
<!-- 此处只需在平时常用的单向传值上加上.sync修饰符 -->
<HelloWorld :startDate.sync="startDate" :endDate.sync="endDate" />
</div>
</template>
<script>
import dataPicker from '@/components/common/data_picker'
export default {
components: {
dataPicker
},
data(){
return{
startDate: new Date(new Date() - 24 * 60 * 60 * 1000 * 30), //开始时间
endDate: new Date(), //结束时间
}
},
}
</script>
子组件
<template>
<div style="display: inline-block;">
<el-date-picker type="datetime" v-model="startDate" placeholder="开始时间"
style="width: 200px"></el-date-picker>
<el-date-picker type="datetime" v-model="endDate" placeholder="结束时间"
style="width: 200px"></el-date-picker>
</div>
</template>
<script>
export default {
props: ['startDate', 'endDate'],
watch: {
startDate(newVal, oldVal) {
this.$emit('update:startDate', newVal);
},
endDate(newVal, oldVal) {
this.$emit('update:endDate', newVal);
},
},
}
</script>
二、待续
四、Message消息提示每次只弹出一个 + 设置全局message弹框的格式
问题描述
- Element UI的Message消息提示是点击一次触发一次的,如果一直点,然后就会出现打开多个的情况。
- 在后台弹消息的时候,也会同时出现
- 设置全局message弹框的格式
- Message距离窗口顶部的偏移量
Message消息提示每次只弹出一个 + 设置全局message弹框的格式
在main.js平级建立文件夹,其中新建message.js
message.js:
/**防止重复点击重复弹出message弹框 */
import {
Message
} from 'element-ui';
let messageInstance = null;
const resetMessage = (options) => {
if (messageInstance) {
messageInstance.close()
}
messageInstance = Message(options)
};
/**设置message弹框的格式 */
['error', 'success', 'info', 'warning'].forEach((item) => {
resetMessage[item] = (msg) => {
return Message({
showClose: true,
duration:2000,
type: item,
message: msg
})
}
})
export const message = resetMessage
main.js:
import {message} from './request/message.js';
Vue.prototype.$message = message;\
### Message距离窗口顶部的偏移量
public.css:
```\
.el-message {\
top: 48%!important\
}\
注意
因为使用了new DonMessage()的原因,所以导致this.message.success('成功提示')或者this.$message.success(options)的方式进行调用。具体参数可以查看官方文档。
五、什么是重定向
就是地址A跳转到地址B啦。百度百科的解释:重定向(Redirect)就是通过各种方法将各种网络请求重新定个方向转到其它位置(如:网页重定向、域名的重定向、路由选择的变化也是对数据报文经由路径的一种重定向)。
六、为什么要进行重定向?什么时候需要重定向?
1)网站调整(如改变网页目录结构);
2)网页被移到一个新地址;
3)网页扩展名改变(如应用需要把.php改成.Html或.shtml)。
这种情况下,如果不做重定向,则用户收藏夹或搜索引擎数据库中旧地址只能让访问客户得到一个404页面错误信息,访问流量白白丧失;再者某些注册了多个域名的网站,也需要通过重定向让访问这些域名的用户自动跳转到主站点等。
七、HTTP协议详解之响应篇
状态代码有三位数字组成,第一个数字定义了响应的类别,且有五种可能取值:
1xx:指示信息–表示请求已接收,继续处理
2xx:成功–表示请求已被成功接收、理解、接受
3xx:重定向–要完成请求必须进行更进一步的操作
4xx:客户端错误–请求有语法错误或请求无法实现
5xx:服务器端错误–服务器未能实现合法的请求
常见状态代码、状态描述、说明:
200 OK //客户端请求成功
400 Bad Request //客户端请求有语法错误,不能被服务器所理解
401 Unauthorized //请求未经授权,这个状态代码必须和WWW-Authenticate报头域一起使用
403 Forbidden //服务器收到请求,但是拒绝提供服务
404 Not Found //请求资源不存在,eg:输入了错误的URL
500 Internal Server Error //服务器发生不可预期的错误
503 Server Unavailable //服务器当前不能处理客户端的请求,一段时间后可能恢复正常
eg:HTTP/1.1 200 OK (CRLF)
八、export与export default区别
1.export default 和export都可以用于导出常量,函数,文件,模块等;
2.可以在模块中通过import+ (常量 | 函数 | 文件 | 模块)名的方式,将其导入,以便能够对其进行使用。
3.在一个文件或者模块中,export,import可以有多个,但是export default只能有一个。
4.通过export方式导出,在导入的时候需要加{},export default不需要在导入的时候加{}
使用export default命令,为模块指定默认输出,这样就不需要知道所要加载模块的变量名。
九、路由传参-使用encodeURI加密参数
在路由切换时页面需要使用地址栏传参,但地址栏会暴露参数的值,然后想到了encodeURI加密参数
比如参数是一个对象obj
obj:{
id: 1,
name: 'Tom'
}
那么需要将参数转换为JSON字符串,在使用encodeURI加密,需要注意的是通过路由跳转的时候会自动解密一次,所以需要加两次密。
- JSON.stringify是日常开发中经常用到的 JSON 对象中的一个方法。
- JSON 对象包含两个方法:一是用于解析成 JSON 对象的 parse();
- 二是用于将对象转换为 JSON 字符串方法的 stringify()
- encodeURI 加密函数
- decodeURI 解密函数
let param = {
id: 1,
name: 'Tom'
}
param = encodeURI(JSON.stringify(param))
param = encodeURI(param) // 第二次加密
this.$router.push({path: `/record-index-city/${param}`,})
解密方式是使用decodeURI
let param = this.$route.params.param
param = JSON.parse(decodeURI(param))
加密后效果:
编辑
十、base64格式的加密与解密
window.atob() 与window.btoa()可以实现对base64格式的数据进行解码和编码,其兼容性是主流浏览器,IE10及以上。
window.atob(encodedStr)只可以解码用btoa方法编码的字符串。
window.btoa():将ascii字符串或二进制数据转换成一个base64编码过的字符串,但该方法不能直接作用于Unicode字符串
<script>
var str = 'RUNOOB'
var enc = window.btoa(str) //编码成base64的
var dec = window.atob(enc) //把base64解码
var res = '编码字符串为: ' + enc + '<br>' + '解码后字符串为: ' + dec
console.log(res)
</script>
当遇到中文时,需要先对中文转码否则会乱码。
var str = btoa(encodeURIComponent("中文汉字"));
//还可以解码回来
decodeURIComponent(atob(enc)) => 中文汉字
十一、vite
1.介绍
编辑
2.速度
Vite 使用了 原生 ES 模块,期间没有涉及模块编译过程,节约了不少时间。
3. 支持框架
vite 支持的框架有 6 种
vanilla:Vanilla JS 是一个快速、轻量级、跨平台的JavaScript框架。Vanilla JS 是世界上最轻量的JavaScript框架(没有之一) —— 其实这玩意就是原生 JS。vue/react:不过多介绍。preact:React 的轻量级替代方案。lit:Lit 是一个简单的库,用于构建快速、轻量级的 Web 组件。(看了一眼语法,感觉还挺好玩的。)svelte:一个不使用Virtual DOM的库 —— 真酷。这个库的作者和Rollup的作者是同一人。
4.Vite与webpack对比
编辑
1、给一个入口文件,通过路口文件找依赖,然后就找到很多模块出来,模块中有js,css和图片总之包含了一大堆文件
2、然后webpack打包,通过loader,plugin等等进行打包;
3、打包完过后就变成非常少量的打包之后的文件,可能是一个也可能是多个,但肯定不是原始的模块文件;
4、打包完后,在开发阶段还要启动一个开发服务器,然后配置服务器,然后启动开发服务器,
5、最终通过访问开发服务器,他就会把打包后的结果给我们
这些过程在vue的开发过程运行项目时的打包其实是在内存中完成的,所以经过这一系列的方式会
使得在开发阶段每次修改代码都会运行打包,如果依赖太多运行就会很慢
5.Vite原理
编辑
1、vite在开发阶段没有打包过程,他是直接启动一个服务器,启动后就啥事也没有做
2、请求一个模块到开发服务器;
3、开发服务器编译模块,根据页面用所需要的依赖去加载文件
4、加载完成后,开发服务器把编译的结果返回给页面
这使得提高了我们在开发阶段运行的效率
6.总结
在vue运行项目时,其内部会执行webpack打包命令,打包完后把项目放入开发服务器中然后启动开发服务器,请求服务器是直接给予打包结果。
而vite是直接启动开发服务器,请求那个模块再对该模块进行实时编译。由于现代浏览器本身就支持ES Moudule,会自动向依赖的Module发出请求。
vite成分利用这一点,将开发环境下的模块文件,就作为浏览器要执行的文件,而不是像webpack那样进行打包合并。
由于vite在启动的时候不需要打包,也就意味着不需要分析模块的依赖,不需要编译,因此启动速度非常块
当浏览器请求某个模块时,在根据需要对模块内容进行编译。这种按需动态编译的方式,极大的缩减了编译时间,项目越复杂,模块越多vite的优势越明显。
在HMR方面,当改动一个模块后,仅需让浏览器重新请求该模块即可,不像webpack那样需要把该模块的
相关依赖模块全部编译一次,效率更高
当需要打包到生产环境时,vite使用传统的rollup进行打包,因此,vite的主要优势在开发阶段。另外,由于vite利用的时ES Module,因此在代码中不可以使用CommonJS
7.支持的浏览器
编辑
通过修改配置最低支持到 es2015,也就是 ES6。处理成 es5:
// vite.config.js
import { defineConfig } from "vite";
// https://github.com/vitejs/vite/tree/main/packages/plugin-legacy
import legacy from "@vitejs/plugin-legacy";
export default defineConfig({
plugins: [
legacy({
targets: ["ie >= 11"],
additionalLegacyPolyfills: ["regenerator-runtime/runtime"],
}),
],
build: {
target: "es2015", // 默认 "modules"
},
});
8.vite 中使用css预处理器等
-
vite 中使用 less/scss/sass/stylus 等 css 预处理器
vitejs.cn/guide/featu… 直接进行安装,不用像 webpack 那样安装 loader 和配置
npm install -D less
复制代码
<style lang="less">
/* use less */
</style>
复制代码
-
vite 中使用 postcss
vitejs.cn/config/#css… vite 中已集成了 postcss,无需再单独安装
-
vite 中使用 autoprefixer
import autoprefixer from "autoprefixer";
export default defineConfig({
css: {
postcss: {
plugins: [autoprefixer],
},
},
});
复制代码
9. vite 打包将 js 和 css 文件夹分离
默认打包后,js 和 css 是全部混在一起的,对强迫症很难受
export default defineConfig({
build: {
rollupOptions: {
output: {
chunkFileNames: "static/js/[name]-[hash].js",
entryFileNames: "static/js/[name]-[hash].js",
assetFileNames: "static/[ext]/[name]-[hash].[ext]",
},
},
},
});
复制代码
10.vite 根目录vite.config.js配置文件读取env变量
在配置文件中直接使用import.meta.env.xxx这样读取是报错的,不过有了上面的异步配置,读取env变量就方便了,我们可以直接使用fs直接获取
.env文件
# PROXY_URL
VITE_PROXY_URL=http://xxxxxx/
# DBPATH_ENV
DBPATH_ENV=XXXX
vite.config.js文件
const fs = require("fs");
const path = require("path");
const { promisify } = require("util");
const fsState = promisify(fs.stat);
const readFile = promisify(fs.readFile);
// 定义一个函数,读取.env文件中的内容
async function getEnvConfig(vite_env_key) {
const envFilePath = path.resolve(__dirname, "./.env");
const [notExistEnvFile, existEnvFile] = await fsState(envFilePath)
.then((data) => [null, data])
.catch((e) => [e, null]);
if (notExistEnvFile || existEnvFile.size <= 0) return;
const envContent = await readFile(envFilePath, "utf8");
if (!envContent) return;
const envContentArr = envContent
.split("\n")
.filter((str) => !str.startsWith("#")) // 过滤掉注释行
.filter(Boolean);
const resultKey = envContentArr.find((item) => item.includes(vite_env_key));
return resultKey ? resultKey.split("=")[1] : null;
}
const resolveConf = async () => {
// 读取 .env 文件中的VITE_PROXY_URL的值
const PROXY_URL = await getEnvConfig("VITE_PROXY_URL");
return {
server: {
host: "0.0.0.0",
port: 3000,
open: true,
proxy: {
"/api": {
target: PROXY_URL,
changeOrigin: true,
// rewrite: (path) => path.replace(/^/api/, ""),
},
},
},
};
};
export default defineConfig(resolveConf());
11.vite 项目index.html中使用env环境变量
如何像webpack项目的html-webpack-plugin那样使用<%= htmlWebpackPlugin.options.title %>,这种方式动态注入变量,vite 中可以使用vite-plugin-html来完成。
- 安装
npm i vite-plugin-html -D
复制代码
- 使用(比如不同环境下动态注入高德地图的
key)
vite.config.js配置文件
import { minifyHtml, injectHtml } from "vite-plugin-html";
const AMAP_KEY = "xxx";
export default defineConfig({
plugins: [
injectHtml({
data: {
title: "hello",
// 高德地图
injectAMapScript: `<script src="https://webapi.amap.com/maps?v=1.4.15&key=${AMAP_KEY}"></script>`,
},
}),
],
});
index.html中使用 ejs 模板引擎动态注入
<!DOCTYPE html>
<html lang="en">
<head>
<title><%- title %></title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.js"></script>
<%- injectAMapScript %>
</body>
</html>
12.引用静态资源和 public 目录
在 vue 文件中template和style中以相对路径和绝对路径两种方式引用静态资源
<template>
<!-- 相对路径 -->
<img src="./assets/img/test.png" />
<!-- 绝对路径 -->
<img src="/src/assets/img/test.png" />
</template>
<style scoped>
#app {
background-image: url("./assets/img/test.png");
}
</style>
- 放在
public目录下的文件应使用绝对路径引用,例如public/icon.png应该使用/icon.png。 public中的资源不应该被JavaScript文件引用。
<img src="/logo.png" />
13.打包生产环境移除 console 和 debugger
export default defineConfig({
build: {
// 生产环境移除console
terserOptions: {
compress: {
drop_console: true,
drop_debugger: true,
},
},
},
});
14.vue-router4.x路由 404 匹配
import { createRouter, createWebHistory } from "vue-router";
const Result = () => import("@/views/Result");
const router = createRouter({
history: createWebHistory(),
routes: [
{
// 注意这里,404页面匹配规则和以前不相同,要采用这种配置方式才行
path: "/:pathMatch(.*)*",
component: Result,
name: "Result",
},
],
});
export default router;
15.vite 启动后关于defineEmits的警告
defineEmitsis a compiler macro and no longer needs to be importe
删掉defineEmits的引用即可
- import { reactive, defineEmits } from 'vue';
+ import { reactive } from 'vue';
// 直接使用defineEmits
const emit = defineEmits(['test1', 'test2']);
emit('test1', 'hello1');
16.vue3 和echarts5动态更新数据报错
- 造成报错的原因是 vue3 中使用 proxy 的方式监听响应式,charts 实例会被在 vue 内部转换成响应式对象
- 在初始化的时候可以使用
markRaw指定为非响应式即可
<template>
<div ref="lineChartDomRef"></div>
</template>
<script setup>
import { markRaw, ref, onMounted } from "vue";
import * as echarts from "echarts";
const lineChartInsRef = ref();
const lineChartDomRef = ref();
onMounted(() => {
// 初始化时使用markRaw,后面使用lineChartInsRef.value实例更新时,就不会报错了
const option = {
// ...
};
lineChartInsRef.value = markRaw(echarts.init(lineChartDomRef.value));
lineChartInsRef.value.setOption(option);
window.addEventListener("resize", () => {
lineChartInsRef.value.resize();
});
});
</script>
17.Vue3.x 使用<script setup>如何设置组件name名称
实可以写两个 script 标签,下面两个可以并存
<script>
import { defineComponent } from "vue";
export default defineComponent({
name: "Test",
});
</script>
<script setup>
// ...
</script>
18.vue3 中的 vue-router4.x 下的 keep-alive 写法
之前这样写,会报警告
<keep-alive>
<RouterView />
</keep-alive>
根据提示,得这样写:
<template>
<router-view v-slot="{ Component }">
<!-- 缓存name名称为aaa和bbb的组件 -->
<keep-alive :include="['aaa', 'bbb']">
<component :is="Component" />
</keep-alive>
</router-view>
</template>
19.vue-router4 监听路由变化 onBeforeRouteUpdate
import { onBeforeRouteUpdate, onBeforeRouteLeave } from 'vue-router';
onBeforeRouteUpdate((to, from, next) => {
if (from.name === 'MachineList') {
...
}
next();
});
复制代码
20. nginx 线上部署,刷新后页面 404
修改 nginx 配置
location / {
root /usr/share/nginx/dist; # 服务默认启动目录
index index.html index.htm; # 默认访问文件
+ try_files $uri /index.html; # 防止浏览器刷新后,页面404
client_max_body_size 100m;
}
21. 参考资料
十二、快速生成vue3代码模板
1、如何创建模板
- 首先在
VScode编辑器中打开,【文件】–>【首选项】–>【用户片段】–>【新代码片段】–> 取名vue.json确定 - 把下列代码放进去
{
// Place your snippets for vue here. Each snippet is defined under a snippet name and has a prefix, body and
// description. The prefix is what is used to trigger the snippet and the body will be expanded and inserted. Possible variables are:
// $1, $2 for tab stops, $0 for the final cursor position, and ${1:label}, ${2:another} for placeholders. Placeholders with the
// same ids are connected.
// Example:
// "Print to console": {
// "prefix": "log",
// "body": [
// "console.log('$1');",
// "$2"
// ],
// "description": "Log output to console"
// }
"Print to console": {
"prefix": "vue3",
"body": [
"<template>",
" <div $1></div>",
"</template>",
"",
"<script setup>",
"import { ref, reactive, toRefs, onBeforeMount, onMounted, watchEffect, computed } from 'vue';",
"import { useStore } from 'vuex';",
"import { useRoute, useRouter } from 'vue-router';",
"/**",
"* 仓库",
"*/",
"const store = useStore();",
"/**",
"* 路由对象",
"*/",
"const route = useRoute();",
"/**",
"* 路由实例",
"*/",
"const router = useRouter();",
"//console.log('1-开始创建组件-setup')",
"/**",
"* 数据部分",
"*/",
"const data = reactive({})",
"onBeforeMount(() => {",
" //console.log('2.组件挂载页面之前执行----onBeforeMount')",
"})",
"onMounted(() => {",
" //console.log('3.-组件挂载到页面之后执行-------onMounted')",
"})",
"watchEffect(()=>{",
"})",
"// 使用toRefs解构",
"// let { } = { ...toRefs(data) } ",
"defineExpose({",
" ...toRefs(data)",
"})",
"",
"</script>",
"<style scoped lang='less'>",
"</style>"
],
"description": "Log output to console"
}
}
2、如何使用模板
- 新建
vue页面,在文章中输入vue3后 - 点击回车即可