Vue Vite 官方文档
https://cn.vitejs.dev
vite.config.js配置 src设置@别名 import { defineConfig } from 'vite' import vue from '@vitejs/plugin-vue' const { resolve } = require ('path' )export default defineConfig ({ plugins : [vue ()], resolve :{ alias :{ '@' : resolve ('src' ) } } })
vite.config.ts 引入 path 模块注意点
安装@types/node
在 tsconfig.node.json
配置
"compilerOptions" : { ... "allowSyntheticDefaultTmports" : true }
Vue CLI 安装 npm install -g @vue/cli yarn global add @vue/cli vue --version
升级 npm upadte -g @vue/cli yarn global upgrade --latest @vue/cli
创建一个项目 vue create hello-world #创建一个名叫 hello-world 的 vue cli项目
配置全局环境变量 .env 全局默认配置文件,不论什么环境都会加载合并
.env.development 开发环境下的配置文件
.env.production 生产环境下的配置文件
属性名必须以VUE_APP_开头
.env.development
文件的加载
根据启动命令vue会自动加载对应的环境,vue是根据文件名进行加载的,所以上面说“不要乱起名,也无需专门控制加载哪个文件”
比如执行npm run serve命令,会自动加载.env.development文件,也有可能是npm run dev,这个得看package.json配置的是啥,具体看下面package.json配置
可通过process.env属性 (全局属性)
.env有的,.env.development也有,就会优先.env.development,.env.development没有的,.env有的,就会有.env的
vue.config.js配置 src设置@别名 module .exports = { chainWebpack : config => { config.resolve .alias .set ('@' , resolve ("src" )) } }
端口设置 module .exports = { devServer : { port : 9090 } }
跨域代理 module .exports = { devServer : { proxy : { '/api' : { target : 'http://127.0.0.1:9001' , ws : true , changeOrigin : true , pathRewrite : { '^/api' : '' } } } } }
package.json配置 环境配置 { "scripts" : { "start" : "vue-cli-service serve --open --mode production" , "dev" : "vue-cli-service serve --open" , "build" : "vue-cli-service build" , "build:prod" : "vue-cli-service build --mode production" , "lint" : "vue-cli-service lint --fix" , "upload" : "vue-cli-service ssh" , "test:unit" : "vue-cli-service test:unit" , "test:e2e" : "vue-cli-service test:e2e --mode development" } }
scripts中dev 说明 需要用 npm run dev 来启动开发环境
如果是serve则是 npm run serve,可以手动添加serve
"scripts" : { "start" : "vue-cli-service serve --open --mode production" , "dev" : "vue-cli-service serve --open" , "serve" : "vue-cli-service serve --open" , "build" : "vue-cli-service build" , "build:prod" : "vue-cli-service build --mode production" , "lint" : "vue-cli-service lint --fix" , "upload" : "vue-cli-service ssh" , "test:unit" : "vue-cli-service test:unit" , "test:e2e" : "vue-cli-service test:e2e --mode development" }
EditorConfig 如果使用的是Vscode,先安装插件 EditorConfig for Visual Studio Code
Axios 安装 npm install axios yarn add axios
创建实例 import axios from 'axios' const instance = axios.create ({ baseURL : 'http://localhost:8080' , timeout : 3000 })export default instance
import axios from '@/utils/MyAxios' import Qs from 'qs' export const findList = ( ) => { return axios ({ url : '' , method : 'get' , params : { } }) }export const add = (data ) => { return axios ({ url : '' , method : 'post' , headers : { 'Content-Type' : 'application/x-www-form-urlencoded' }, data : Qs .stringify (data) }) }
TypeScript 基础类型 布尔值 let isDone : boolean = false ;
数字 let decLiteral : number = 6 ;let hexLiteral : number = 0xf00d ;let binaryLiteral : number = 0b1010 ;let octalLiteral : number = 0o744 ;
字符串 let name : string = "bob" ; name = 'smith' ;
还可以使用模版字符串 ,它可以定义多行文本和内嵌表达式。 这种字符串是被反引号包围( ```),并且以${ expr }
这种形式嵌入表达式
let name : string = `Gene` ;let age : number = 37 ;let sentence : string = `Hello , my name is ${ name } . I'll be ${ age + 1 } years old next month.` ;
这与下面定义相同
let sentence : string = "Hello, my name is " + name + ".\n\n" + "I'll be " + (age + 1 ) + " years old next month." ;
数组 可以在元素类型后面接上[]
,表示由此类型元素组成的一个数组:
let list : number [] = [1 , 2 , 3 ];
或者
let list : Array <number > = [1 , 2 , 3 ];
元组 元组类型允许表示一个已知元素类型和类型的数组,各元素的类型不必相同。比如,你可以定义一对值分别为string
和number
类型的元组。
let x : [string , number ]; x = ['hello' , 10 ];
枚举 使用enum
类型可以为一组数组赋予友好的名字。
enum Color {Red , Green , Blue }let c : Color = Color .Green ;
默认情况下,从0
开始为元素编号。 你也可以手动的指定成员的数值。 例如,我们将上面的例子改成从 1
开始编号:
enum Color {Red = 1 , Green , Blue }let c : Color = Color .Green ;
或者,全部都采用手动赋值:
enum Color {Red = 1 , Green = 2 , Blue = 4 }let c : Color = Color .Green ;
枚举类型提供的一个便利是你可以由枚举的值得到它的名字。 例如,我们知道数值为2
,但是不确定它映射到Color
的哪个名字,我们可以查找相应的名字:
enum Color {Red = 1 , Green , Blue }let colorName : string = Color [2 ];console .log (colorName);
Any 不希望类型检查器对这些值进行检查而是直接让它们通过编译阶段的检查。
那么可以使用 any
类型来标记这些变量:
let notSure : any = 4 ; notSure = "maybe a string instead" ; notSure = false ;
在对现有代码进行改写的时候,any
类型是十分有用的,它允许你在编译时可选择地包含或移除类型检查。 你可能认为 Object
有相似的作用,就像它在其它语言中那样。 但是 Object
类型的变量只是允许你给它赋任意值 - 但是却不能够在它上面调用任意的方法,即便它真的有这些方法:
let notSure : any = 4 ; notSure.ifItExists (); notSure.toFixed (); let prettySure : Object = 4 ; prettySure.toFixed ();
当只知道一部分数据的类型时,any
类型也是有用的。比如,有一个数组,它包含了不同类型的数据:
let list : any [] = [1 , true , "free" ];console .log (list[1 ]); list[1 ] = 100 ;
比较像元组
Void void
类型与any
类型相反,它表示没有任何类型。当一个函数没有返回值时,通常会见到其返回类型是void
:
function warnUser ( ): void { console .log ("This is my warning message" ); }
声明一个void
类型的变量没有什么大用,因为你只能为它赋予undefinded
和null
:
let unusable : void = undefin
VueRouter Vuex Vuex是一个专为Vue.js应用程序开发的状态管理模式 。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
状态(state)通俗说就是数据(data)。
可用于父子组件数据传值。最主要用于解决兄弟组件数据传值,以往通过将数据存储到localStorage中。
安装
state state相当于组件中的data,专门用来存放全局的数据
在src
下创建 store
文件夹
创建index.js
文件
import { createStore } from 'vuex' ;export default createStore ({ state : { num : 0 }, getters : {}, mutations : { increment (state) { state.num ++ } }, })
修改入口文件main.js
import { createApp } from 'vue' import App from './App.vue' import store from '@/store' createApp (App ).use (store).mount ('#app' )
在src
下创建views
文件夹
创建About.vue
<template> <div> <h2>About 页面的 数字: {{ num }}</h2> </div> </template> <script> export default { // 计算属性 computed: { num() { return this.$store.state.num } } } </script> <style> </style>
创建Home.vue
<template> <div> <h2>Home 页面的 数字: {{ $store.state.num }}</h2> </div> </template> <script> export default { data() { return{ } }, } </script> <style> </style>
在components
文件夹下创建Btn.vue
<template> <div> <button @click="changeNum">点击加1</button> </div> </template> <script> export default { methods: { changeNum() { // 改变状态 执行mutations下increment this.$store.commit('increment') } } } </script> <style> </style>
修改主组件App.vue
<script> // This starter template is using Vue 3 <script setup> SFCs // Check out https://v3.vuejs.org/api/sfc-script-setup.html#sfc-script-setup import HelloWorld from './components/HelloWorld.vue' import Home from '@/views/Home.vue' import About from '@/views/About.vue' import Btn from '@/components/Btn.vue' export default { components: { Home, About, Btn }, data() { return{ } }, methods: { }, computed: { } } </script> <template> <div> <Home></Home> <About></About> <Btn></Btn> </div> </template> <style> #app { font-family: Avenir, Helvetica, Arial, sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; text-align: center; color: #2c3e50; margin-top: 60px; } </style>
getters getters相当于组件中的computed,getters是全局的,computed是组件内部的
修改index.js
import { createStore } from 'vuex' ;export default createStore ({ state : { num : 0 }, getters : { getNum (state ) { return state.num } }, mutations : { increment (state) { state.num ++ } }, })
修改About.vue
<template> <div> <h2>About 页面的 数字: {{ $store.getters.getNum }}</h2> </div> </template> <script> export default { computed: { num() { return this.$store.state.num } } } </script> <style> </style>
mutations mutations相当于组件中的methods,但是它不能使用异步方法(定时器、axios)
修改index.js
import { createStore } from 'vuex' ;export default createStore ({ state : { num : 0 }, getters : { getNum (state ) { return state.num } }, mutations : { increment (state, payload) { state.num +=payload ? payload : 1 } }, })
修改Btn.vue
<template> <div> <button @click="changeNum(n)">点击加1</button> <br> <input v-model.number="n"> {{ $store.state.num }} </div> </template> <script> export default { data() { return{ n: 2 } }, methods: { // 调用store中的mutations里的increment方法 // 传参的话,使用payload changeNum(n) { console.log(n) this.$store.commit('increment',n) } } } </script> <style> </style>
actions actions专门用来处理异步,实际修改状态值的,依然是mutations
修改index.js
import { createStore } from 'vuex' ;export default createStore ({ state : { num : 0 }, getters : { getNum (state ) { return state.num } }, mutations : { increase (state, payload) { state.num += payload ? payload : 1 }, clear (state) { state.num = 0 }, decrease (state, payload) { if (state.num === 0 ) { state.num = 0 } else { state.num -= payload ? payload : 1 } } }, actions : { decreaseAync (context, num) { context.commit ('decrease' , num) } } })
修改Btn.vue
<template> <div> <button @click="changeNum(n)">点击加1</button> <button @click="subNum(n)">点击减1</button> <button @click="clearNum">清空</button> <br> <input v-model.number="n"> </div> </template> <script> export default { data() { return{ n: 1 } }, methods: { // 调用store中的mutations里的increment方法 // 传参的话,使用payload changeNum(n) { this.$store.commit('increase', n) }, clearNum() { this.$store.commit('clear') }, subNum(n) { this.$store.dispatch('decreaseAync', n) } } } </script> <style> </style>
Pug.js Pug(之前称为Jade)是一种简洁而灵活的模板引擎,用于构建HTML。Pug使用缩进和标记替代了常见的HTML标记,这使得它更易于阅读和编写。它也提供了一些功能,如变量、循环和条件语句等,可以帮助开发人员更轻松地构建动态的网页。
特性 属性 如果一个标签有多个属性,可使用 分行 或 逗号
el-button (v-if="ifShow" type="size" size="small" @click ="doClidk" ) 点击el-button (v-if="ifShow" ,type="size" ,size="small" ,@click ="doClidk" ) 点击el-button (v-if="ifShow" type="size" size="small" @click ="doClick" ) 点击
注释
// 一些内容 p foo p bar<p > foo</p > <p > bar</p >
p foo p bar <p >foo </p > <p >bar </p >
body // 一堆 文字 进行中<body > </body >
Vue 2集成Pug.js Vue CLI安装Pug.js
npm i -D pug pug-html-loader pug-plain-loader
vue.config.js配置
module .exports = { chainWebpack : config => { config.module .rule ('pug' ) .test (/\.pug$/ ) .use ('pug-html-loader' ) .loader ('pug-html-loader' ) .end () } }
使用前
<template> <div class="app-container"> <!-- 搜索栏 --> <el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch"> <el-form-item label="昵称"> <el-input v-model="queryParams.nickName" placeholder="请输入用户昵称" clearable style="..." @keyup.enter.native="handleQuery" /> </el-form-item> <el-form-item label="状态" prop="status"> <el-select v-model="queryParams.status" placeholder="请选择状态" clearable style="..." > <el-option v-for="dict in dict.type.sys_normal_disable" :key="dict.value" :label="dict.label" :value="dict.value" /> </el-select> </el-form-item> <el-form-item> <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button> <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button> </el-form-item> </el-form> <!-- 工具栏按钮 --> <el-row :gutter="10" class="mb8"> <el-col :span="1.5"> <el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd" >新增</el-button> </el-col> <el-col :span="1.5"> <el-button type="success" plain icon="el-icon-edit" size="mini" :disabled="single" @click="handleUpdate" >修改</el-button> </el-col> <el-col :span="1.5"> <el-button type="danger" plain icon="el-icon-delete" size="mini" :disabled="multiple" @click="handleDelete" >删除</el-button> </el-col> <el-col :span="1.5"> <el-button type="warning" plain icon="el-icon-download" size="mini" @click="handleExport" >导出</el-button> </el-col> </el-row> <!-- 表格 --> <el-table v-loading="loading" :data="list" @selection-change="handleSelectionChange"> <el-table-column type="selection" width="55" align="center" /> <el-table-column prop="userId" v-if="false"></el-table-column> <el-table-column label="用户昵称" align="center" prop="nickName" /> <el-table-column label="用户头像" align="center" prop="pic" /> <el-table-column label="状态" align="center" prop="status"> <template slot-scope="scope"> <dict-tag :options="dict.type.sys_normal_disable" :value="scope.row.status" /> </template> </el-table-column> <el-table-column label="注册时间" align="center" prop="userRegtime"></el-table-column> <el-table-column label="操作" align="center" class-name="small-padding fixed-width"> <el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)" v-hasPermi="['system:dict:edit']" >修改</el-button> <el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)" v-hasPermi="['system:dict:remove']" >删除</el-button> </el-table-column> </el-table> <pagination v-show="total>0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize" @pagination="getList" /> <el-dialog :title="title" :visible.sync="open" width="500px" append-to-body> <el-form ref="form" :model="form" :rules="rules" label-width="80px"> <el-form-item label="用户头像" prop="pic"> <!-- <el-input v-model="form.pic" placeholder="请输入" /> --> </el-form-item> <el-form-item label="用户昵称" prop="nickName"> <el-input v-model="form.nickName" placeholder="请输入用户昵称" /> </el-form-item> <el-form-item label="状态" size="mini" prop="status"> <el-radio v-for="dict in dict.type.sys_normal_disable" :key="dict.value" :label="dict.value" >{{ dict.label }}</el-radio> </el-form-item> </el-form> <div slot="footer" class="dialog-footer"> <el-button type="primary" @click="submitForm">确 定</el-button> <el-button @click="cancel">取 消</el-button> </div> </el-dialog> </div> </template>
使用后
<template lang ="pug "> .app-container el-form (:model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" ) el-form-item (label="昵称" ) el-input ( v-model="queryParams.nickName" placeholder="请输入用户昵称" clearable style="..." @keyup .enter.native="handleQuery" ) el-form-item (label="状态" prop="status" ) el-select ( v-model="queryParams.status" placeholder="请选择状态" clearable style="..." ) el-option ( v-for="dict in dict.type.sys_normal_disable" :key="dict.value" :label="dict.label" :value="dict.value" ) el-form-item el-button (type="primary" icon="el-icon-search" size="mini" @click ="handleQuery" ) 搜索 el-button (icon="el-icon-refresh" size="mini" @click ="resetQuery" ) 重置 el-row .mb8 (:gutter="10" ) el-col (:span="1.5" ) el-button ( type="primary" plain icon="el-icon-plus" size="mini" @click ="handleAdd" ) 新增 el-col (:span="1.5" ) el-button ( type="success" plain icon="el-icon-edit" size="mini" :disabled="single" @click ="handleUpdate" ) 修改 el-col (:span="1.5" ) el-button ( type="danger" plain icon="el-icon-delete" size="mini" :disabled="multiple" @click ="handleDelete" ) 删除 el-col (:span="1.5" ) el-button ( type="warning" plain icon="el-icon-download" size="mini" @click ="handleExport" ) 导出 el-table (v-loading="loading" :data="list" @selection-change ="handleSelectionChange" ) el-table-column (type="selection" width="55" align="center" ) el-table-column (prop="userId" v-if="false" ) el-table-column (label="用户昵称" align="center" prop="nickName" ) el-table-column (label="用户头像" align="center" prop="pic" ) el-table-column (label="状态" align="center" prop="status" ) template (slot-scope="scope" ) dict-tag (:options="dict.type.sys_normal_disable" :value="scope.row.status" ) el-table-column (label="注册时间" align="center" prop="userRegtime" ) el-table-column (label="操作" align="center" class-name="small-padding fixed-width" ) el-button ( size="mini" type="text" icon="el-icon-edit" @click ="handleUpdate(scope.row)" v-hasPermi="['system:dict:edit']" ) 修改 el-button ( size="mini" type="text" icon="el-icon-delete" @click ="handleDelete(scope.row)" v-hasPermi="['system:dict:remove']" ) 删除 pagination ( v-show="total>0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize" @pagination ="getList" ) el-dialog ( :title="title" :visible.sync="open" width="500px" append-to-body ) el-form ( ref="form" :model="form" :rules="rules" label-width="80px" ) el-form-item (label="用户头像" prop="pic" ) el-form-item (label="用户昵称" prop="nickName" ) el-input (v-model="form.nickName" placeholder="请输入用户昵称" ) el-form-item (label="状态" size="mini" prop="status" ) el-radio ( v-for="dict in dict.type.sys_normal_disable" :key="dict.value" :label="dict.value" ) {{ dict .label }} .dialog-footer (slot="footer" ) el-button (type="primary" @click ="submitForm" ) 确 定 el-button (@click ="cancel" ) 取 消 </template >
Vben Admin 官方文档
https://vvbin.cn/doc-next/
D2Admin API封装 /src/api
axios实例 service.js
自定义API modules目录
sys.user.api.js
const baseURL = '/admin' export default ({ service, request, serviceForMock, requestForMock, mock, faker, tools }) => ({ SYS_USER_LOGIN (data = {}) { return request ({ url : baseURL + '/login/login' , method : 'post' , data : data }) }, FIND_USER_LIST (data = {}) { return request ({ url : baseURL + '/user/getAll' , method : 'post' , data : data }) }, REFRESH_MSG () { return request ({ url : baseURL + '/user/refreshmsg' , method : 'get' }) }, ADD_USER (data = {}) { return request ({ url : baseURL + '/user/add' , method : 'post' , data : data }) }, UPDATE_USER (data = {}) { return request ({ url : baseURL + '/user/update' , method : 'put' , data : data }) }, DELETE_USER_BY_ID (data = {}) { return request ({ url : baseURL + '/user/delete' , method : 'delete' , data : data }) } })
sys.menu.api.js
const baseURL = '/admin' export default ({ request }) => ({ MENU_CURRENT (data = {}) { return request ({ url : baseURL + '/menu/get/current/menutree' , method : 'get' }) }, ROUTER_CURRENT (data = {}) { return request ({ url : baseURL + '/menu/get/current/vuerouter' , method : 'get' }) } })
登录逻辑 登录页面
/src/views/system/login/page.vue
page.vue
<script> methods: { // 通过Vuex 展开action 分发login ...mapActions('d2admin/account', [ 'login' ]), // 提交登录信息 submit () { this.$refs.loginForm.validate((valid) => { if (valid) { // 登录 // 注意 这里的演示没有传验证码 // 具体需要传递的数据请自行修改代码 // 调用action this.login({ username: this.formLogin.username, password: this.formLogin.password }) .then(() => { // 重定向对象不存在则返回顶层路径 this.$router.replace(this.$route.query.redirect || '/') }) } else { // 登录表单校验失败 this.$message.error('表单校验失败,请检查') } }) } </script>
/src/store/modules/d2admin/modules/account.js
account.js
actions : { async login ({ dispatch }, { username = '' , password = '' , to = '/' } = {}) { const res = await api.SYS_USER_LOGIN ({ username, password }) util.cookies .set ('uuid' , res.uuid ) util.cookies .set ('token' , res.token ) await dispatch ('d2admin/user/set' , { name : res.name }, { root : true }) await dispatch ('load' ) await dispatch ('updateCache' , { to : to }) }, }
动态菜单 修改main.js
入口文件
new Vue ({ router, store, i18n, render : h => h (App ), created () { this .$store .commit ('d2admin/page/init' , frameInRoutes) this .$store .dispatch ('d2admin/menu/get' ) },
注释掉以下 mutation
所有逻辑在Vuex中完成
到 /src/store/modules/d2admin/modules/menu.js
menu.js
import setting from '@/setting.js' import api from '@/api' import { uniqueId } from 'lodash' function supplementPath (menu) { return menu.map (e => ({ ...e, path : e.path || uniqueId ('d2-menu-empty-' ), ...e.children ? { children : supplementPath (e.children ) } : {} })) }export default { namespaced : true , state : { header : [], aside : [], asideCollapse : setting.menu .asideCollapse , asideTransition : setting.menu .asideTransition }, actions : { async asideCollapseSet ({ state, dispatch }, collapse) { state.asideCollapse = collapse await dispatch ('d2admin/db/set' , { dbName : 'sys' , path : 'menu.asideCollapse' , value : state.asideCollapse , user : true }, { root : true }) }, async asideCollapseToggle ({ state, dispatch }) { state.asideCollapse = !state.asideCollapse await dispatch ('d2admin/db/set' , { dbName : 'sys' , path : 'menu.asideCollapse' , value : state.asideCollapse , user : true }, { root : true }) }, async set ({ state, dispatch }, aside) { aside = supplementPath (Array .from (await api.MENU_CURRENT ())) state.aside = aside console .log ('---set-------' ) await dispatch ('d2admin/db/set' , { dbName : 'sys' , path : 'menu.aside' , value : aside, user : true }, { root : true }) await dispatch ('d2admin/search/init' , aside, { root : true }) }, async get ({ state, dispatch, commit }) { state.aside = await dispatch ('d2admin/db/get' , { dbName : 'sys' , path : 'menu.aside' , defaultValue : [], user : true }, { root : true }) commit ('asideSet' , state.aside ) await dispatch ('d2admin/search/init' , state.aside , { root : true }) }, async asideTransitionSet ({ state, dispatch }, transition) { state.asideTransition = transition await dispatch ('d2admin/db/set' , { dbName : 'sys' , path : 'menu.asideTransition' , value : state.asideTransition , user : true }, { root : true }) }, async asideTransitionToggle ({ state, dispatch }) { state.asideTransition = !state.asideTransition await dispatch ('d2admin/db/set' , { dbName : 'sys' , path : 'menu.asideTransition' , value : state.asideTransition , user : true }, { root : true }) }, async asideLoad ({ state, dispatch }) { const menu = await dispatch ('d2admin/db/get' , { dbName : 'sys' , path : 'menu' , defaultValue : setting.menu , user : true }, { root : true }) state.asideCollapse = menu.asideCollapse !== undefined ? menu.asideCollapse : setting.menu .asideCollapse state.asideTransition = menu.asideTransition !== undefined ? menu.asideTransition : setting.menu .asideTransition } }, mutations : { headerSet (state, menu) { state.header = menu }, asideSet (state, menu) { state.aside = menu } } }
到 /src/store/modules/d2admin/modules/account.js
account.js
新增一个action updateCache
updateCache ({ dispatch }, { to = '/' }) { return new Promise ((resolve, reject ) => { dispatch ('d2admin/menu/set' , {}, { root : true }) api.ROUTER_CURRENT ().then (result => { dispatch ('d2admin/router/load' , { to : to, focus : true , data : result }, { root : true }) }) resolve () }) }
api请求
/src/api/modules/sys.menu.api.js
sys.menu.api.js
const baseURL = '/admin' export default ({ request }) => ({ MENU_CURRENT (data = {}) { return request ({ url : baseURL + '/menu/get/current/menutree' , method : 'get' }) }, ROUTER_CURRENT (data = {}) { return request ({ url : baseURL + '/menu/get/current/vuerouter' , method : 'get' }) } })
响应报文
{ "code" : 0 , "msg" : "请求成功" , "data" : [ { "id" : 1 , "title" : "信评中心" , "enname" : "crManager" , "parentId" : -1 , "icon" : "folder-o" , "sort" : 1 , "isEnabled" : 1 , "children" : [ { "id" : 2 , "title" : "信评工作台" , "enname" : "crWorkbench" , "parentId" : 1 , "icon" : "folder-o" , "sort" : 2 , "isEnabled" : 1 , "children" : [ { "id" : 3 , "title" : "信用评级待办" , "parentId" : 2 , "icon" : "folder-o" , "sort" : 3 , "isEnabled" : 1 } ] } , { "id" : 4 , "title" : "信用评级启动" , "parentId" : 1 , "icon" : "folder-o" , "isEnabled" : 1 , "children" : [ { "id" : 5 , "title" : "个人评级" , "enname" : "cr_person_launch" , "parentId" : 4 , "path" : "/cr_person_launch" , "component" : "cr/cr_launch/cr_person_launch/cr_person_launch" , "icon" : "folder-o" , "isEnabled" : 1 } , { "id" : 6 , "title" : "企业评级" , "enname" : "cr_business_launch" , "parentId" : 4 , "path" : "/cr_business_launch" , "component" : "cr/cr_launch/cr_business_launch/cr_business_launch" , "icon" : "folder-o" , "isEnabled" : 1 } ] } , { "id" : 7 , "title" : "信用评级查询" , "parentId" : 1 , "icon" : "folder-o" , "isEnabled" : 1 } , { "id" : 8 , "title" : "评级模板管理" , "parentId" : 1 , "icon" : "folder-o" , "isEnabled" : 1 } ] } , { "id" : 9 , "title" : "系统管理" , "parentId" : -1 , "icon" : "folder-o" , "isEnabled" : 1 , "children" : [ { "id" : 10 , "title" : "用户管理" , "enname" : "usertable" , "parentId" : 9 , "path" : "/usertable" , "component" : "user/usertable" , "icon" : "folder-o" , "isEnabled" : 1 } , { "id" : 11 , "title" : "菜单管理" , "enname" : "menu" , "parentId" : 9 , "path" : "/menu" , "component" : "menu/menu" , "isEnabled" : 1 } , { "id" : 16 , "title" : "权限管理" , "enname" : "role" , "parentId" : 9 , "path" : "/role" , "component" : "role/role" , "isEnabled" : 1 } ] } , { "id" : 12 , "title" : "演示页面" , "parentId" : -1 , "isEnabled" : 1 , "children" : [ { "id" : 13 , "title" : "页面 1" , "enname" : "page1" , "parentId" : 12 , "path" : "/page1" , "component" : "demo/page1" , "isEnabled" : 1 } , { "id" : 14 , "title" : "页面 2" , "enname" : "page2" , "parentId" : 12 , "path" : "/page2" , "component" : "demo/page2" , "isEnabled" : 1 } , { "id" : 15 , "title" : "页面 3" , "enname" : "page3" , "parentId" : 12 , "path" : "/page3" , "component" : "demo/page3" , "isEnabled" : 1 } ] } ] , "type" : "success" }
包管理器 npm包管理器 # 淘宝镜像 # 永久生效 npm config set registry https://registry.npmmirror.com# 查看镜像 npm config get registry# 暂时生效 npm install --registry https://registry.npmmirror.com
CentOS7安装node js # 首先下载nodejs安装包 # 解压 注意这不是gz压缩 tar -xvf node-v14.18.1-linux-x64.tar.xz mv node-v14.18.1-linux-x64.tar.xz /usr/node vim /etc/profile# node export NODE_HOME=/usr/node export PATH=$PATH:$NODE_HOME/bin source /etc/profile# 查看node 版本 node -v npm -v# 安装yarn npm install -g yarn# 软连接,防止找不到环境 ln -s "/usr/node/bin/node" "/usr/local/bin/node" ln -s "/usr/node/bin/npm" "/usr/local/bin/npm" ln -s "/usr/node/bin/yarn" "/usr/local/bin/yarn"
yarn包管理器 # PowerShell yarn : 无法加载文件 C:\Users\Admin\AppData\Roaming\npm\yarn.ps1,因为在此系统因为在此系统上禁止运行脚本。 # 以管理员方式运行powershell set-ExecutionPolicy RemoteSigned# 查看当前镜像 yarn config get registry# https://registry.yarnpkg.com # 更换镜像 yarn config set registry https://registry.yarnpkg.com # 查看镜像是否成功 yarn config get registry# 全局安装PTE脚手架 yarn global add generator-pte-cli# 安装依赖 yarn# 运行项目 yarn dev # yarn run dev# 部署 # 具体看 package.json yarn deploy yarn build# 移除依赖 yarn remove pte-ui# 安装依赖 yarn add pte-ui@2.1.54
安装node-sass # 安装gyp npm install -g node-gyp npm config set python python2.7 npm config set msvs_version 2017 yarn
yarn和npm命令对比
npm
yarn
注释
npm init
yarn init
初始化项目
npm install
yarn
安装全部依赖
npm install react –save
yarn add react
安装某个依赖,保存到dependencies
npm uninstall react –save
yarn remove react
移除某个依赖
npm install react –save-dev
yarn add react –dev
安装某依赖,保存到devDependencies
npm update react –save
yarn upgrade react
更新某个依赖包
npm install react –global
yarn global add react
全局安装某个依赖
npm install –save react axios
yarn add react axios
同时安装多个依赖包
npm install [package]@[version]
yarn add [package]@[version]
安装指定版本的包
npm rebuild
yarn install –force
重新下载所有包
快速删除 node_modules 利用 npm 包 rimraf 快速删除 node_modules
文件夹
先全局安装 npm 包
使用
npx npm大家都知道,是node的包管理器,npx虽然也见过,但似乎较少用过,那npx到底是什么呢?
npx是npm5.2.0版本新增的一个工具包,定义为npm包的执行者,相比npm,npx会自动安装依赖包并执行某个命令。
假如我们要用create-react-app脚手架创建一个react项目,常规的做法是先安装create-react-app,然后才能使用create-react-app执行命令进行项目创建。
// 第一步 npm i -g create-react-app // 第二步 create-react-app my-react-app
有了npx后,我们可以省略安装create-react-app这一步。
// 使用npx npx create-react-app my-react-app
npx会在当前目录下的./node_modules/.bin
里去查找是否有可执行的命令,没有找到的话再从全局里查找是否有安装对应的模块,全局也没有的话就会自动下载对应的模块,如上面的create-react-app,npx会将create-react-app下载到一个临时目录,用完即删,不会占用本地资源。
npm自带npx,可以直接使用,如果没有可以手动安装一下:npm i -g npx
npx命令参数 –no-install –no-install 告诉npx不要自动下载,也意味着如果本地没有该模块则无法执行后续的命令。
npx --no-install create-react-app my-react-app // not found: create-react-app
–ignore-existing –ignore-existing 告诉npx忽略本地已经存在的模块,每次都去执行下载操作,也就是每次都会下载安装临时模块并在用完后删除。
-p -p 用于指定npx所要安装的模块,它可以指定某一个版本进行安装:
npx -p node@12.0.0 node index.js
-p 还可以用于同时安装多个模块:
npx -p lolcatjs -p cowsay [command]
-c -c 告诉npx所有命令都用npx解释。
npx -p lolcatjs -p cowsay 'cowsay hello | lolcatjs'
这样运行会报错,因为第一项命令cowsay hello默认有npx解释,但第二项命令localcatjs会有shell解释,此时lolcatjs并没有全局安装,所以就报错了。这时候可以用 -c 参数来解决。
npx -p lolcatjs -p cowsay -c 'cowsay hello | lolcatjs'
加密解密 jsencrypt 安装 npm install jsencrypt --save-dev
引入
import jsencrypt from 'jsencrypt'
Chrome 插件开发 项目配置 import pkg from "../package.json" ;const manifest : chrome.runtime .Manifest = { manifest_version : 3 , name : pkg.name , version : pkg.version , description : pkg.description , host_permissions : ["*://*/*" ], background : { service_worker : "src/entries/background/main.ts" , }, icons : { "16" : "images/icon16.png" , "24" : "images/icon24.png" , "32" : "images/icon32.png" , "48" : "images/icon48.png" , }, action : { default_popup : "index.html" , }, options_ui : { page : "src/entries/options/index.html" , open_in_tab : false , }, permissions : ["activeTab" , "scripting" , "declarativeContent: " ], content_security_policy : { extension_pages : "script-src 'self'; object-src 'self';" , }, };export default manifest;
内容脚本 需要操作dom,并且不需要一直在后台运行,只需要再打开网页的时候运行
使用内容脚本(content_scripts
)的方式运行插件即可
内容脚本(content_scripts
)的特性:
在页面打开,或者页面加载结束,或者页面空闲的时候注入
共享页面dom,也就是说可以操作页面的dom
JS隔离的,插件中的js定义并不会影响页面的js,也不能引用页面中的js变量、函数
开始使用content_scripts
:
content_scripts
有多种使用方式:
静态注入。在manifest.json
文件中声明
动态注入。chrome.scripting.registerContentScripts
编码注入。chrome.scripting.executeScript
一般使用静态注入。
在manifest.ts
文件中添加一下content_scripts
配置
content_scripts : [ { matches : ["<all_urls>" ], js : ["src/entries/content/content-script.js" ], run_at : "document_end" , }, ],
content_scripts
还有动态注入的方式,其实就是通过调用api的方法来注入,如下示例代码:
chrome.scripting .registerContentScripts ([{ id : "session-script" , js : ["content.js" ], persistAcrossSessions : false , matches : ["*://example.com/*" ], runAt : "document_start" , }]) .then (() => console .log ("registration complete" )) .catch ((err ) => console .warn ("unexpected error" , err))
动态注入可以scripts注入的时机更可控、或者可以更新、删除content_scripts。
content_scripts
属性是一个数组,也就是说我们可以配置多个脚本规则,数组的每个元素包含多个属性:
matches 指定此内容脚本将被注入到哪些页面。必填
js 要注入匹配页面的 JavaScript 文件列表。选填
css 要注入匹配页面的 CSS 文件列表。选填
run_at 指定何时应将脚本注入页面。有三种类型,document_start
,document_end
,document_idle
。默认为document_idle。选填
页面通信
由于content_scripts
是在网页中运行的,而非在扩展的上下文中,因此它们通常需要某种方式与扩展的其余部分进行通信。 扩展页面(options_page,bakcground,popup
)和内容脚本(content_scripts
)之间的通信通过使用消息传递进行。 任何一方都可以侦听从另一端发送的消息,并在同一通道上做出响应。消息可以包含任何有效的 JSON 对象(空值、布尔值、数字、字符串、数组或对象)。
发送 从内容脚本(content_scripts
) 发送到 扩展页面(options_page,bakcground,popup
),代码示例:
(async () => { const response = await chrome.runtime .sendMessage ({greeting : "hello" }); console .log (response); })();
从扩展页面(options_page,bakcground,popup
)发送到 内容脚本(content_scripts
) 代码示例:
(async () => { const [tab] = await chrome.tabs .query ({active : true , lastFocusedWindow : true }); const response = await chrome.tabs .sendMessage (tab.id , {greeting : "hello" }); console .log (response); })();
接受 接收消息的方法都是一样的,通过runtime.onMessage
事件侦听器来处理消息
chrome.runtime .onMessage .addListener ( function (request, sender, sendResponse ) { console .log (sender.tab ? "from a content script:" + sender.tab .url : "from the extension" ); if (request.greeting === "hello" ) sendResponse ({farewell : "goodbye" }); } );