五一期间,花了 3 天时间,边学 Vue3 和 Vite2,边重构自己的项目,终于都用 Vue3 + TypeScript + Vite2 + Vuex4 + Vue-Router4 + element-plus 重构完啦!
终于完成一项心心念念的 年度目标了 ✌️
项目地址:
/biaochenxuying/blog-vue-typescript
效果
效果图:
pc 端
移动端
完整效果请看:
功能
已经完成功能
[x] 登录
[x] 注册
[x] 文章列表
[x] 文章归档
[x] 标签
[x] 关于
[x] 点赞与评论
[x] 留言
[x] 历程
[x] 文章详情(支持代码语法高亮)
[x] 文章详情目录
[x] 移动端适配
[x] github 授权登录
前端主要技术
所有技术都是当前最新的。
vue:^3.0.5
typescript : ^4.1.3
element-plus: ^1.0.2-beta.41
vue-router : ^4.0.6
vite: ^2.2.3
vuex: ^4.0.0
axios: ^0.21.1
highlight.js: ^10.7.2
marked:^2.0.3
1. 初化化项目
用 vite-app 创建项目
yarncreatevite-app<project-name>#或者npminitvite-app<project-name>
然后按照提示操作即可!
进入项目,安装依赖
cd<project-name>yarn#或npmi
运行项目
yarndev
打开浏览器 http://localhost:3000 查看
2. 引入 TypeScript
在创建项目的时候可以 TypeScript 的,如果你选择了 TypeScript ,可以忽略第 2 个步骤。
加入 ts 依赖
yarnadd--devtypescript
在 项目根目录下创建 TypeScript 的配置文件 tsconfig.json
{"compilerOptions":{//允许从没有设置默认导出的模块中默认导入。这并不影响代码的输出,仅为了类型检查。"allowSyntheticDefaultImports":true,//解析非相对模块名的基准目录"baseUrl":".","esModuleInterop":true,//从tslib导入辅助工具函数(比如__extends,__rest等)"importHelpers":true,//指定生成哪个模块系统代码"module":"esnext",//决定如何处理模块。"moduleResolution":"node",//启用所有严格类型检查选项。//启用--strict相当于启用--noImplicitAny,--noImplicitThis,--alwaysStrict,//--strictNullChecks和--strictFunctionTypes和--strictPropertyInitialization。"strict":true,//生成相应的 .map文件。"sourceMap":true,//忽略所有的声明文件(*.d.ts)的类型检查。"skipLibCheck":true,//指定ECMAScript目标版本"target":"esnext",//要包含的类型声明文件名列表"types":[],"isolatedModules":true,//模块名到基于 baseUrl的路径映射的列表。"paths":{"@/*":["src/*"]},//编译过程中需要引入的库文件的列表。"lib":["ESNext","DOM","DOM.Iterable","ScriptHost"]},"include":["src/**/*.ts","src/**/*.tsx","src/**/*.vue","tests/**/*.ts","tests/**/*.tsx"],"exclude":["node_modules"]}
在 src 目录下新加 shim.d.ts 文件
/*eslint-disable*/importtype{DefineComponent}from'vue'declaremodule'*.vue'{constcomponent:DefineComponent<{},{},any>exportdefaultcomponent}
把 main.js 修改成 main.ts
在根目录,打开 Index.html
<scripttype="module"src="/src/main.js"></script>修改为:<scripttype="module"src="/src/main.ts"></script>
3. 引入 eslint
安装 eslint prettier 依赖
@typescript-eslint/parser @typescr ipt-eslint/eslint-plugin
为 eslint 对 typescript 支持。
yarnadd--deveslintprettiereslint-config-prettiereslint-plugin-prettiereslint-plugin-vue@typescript-eslint/parser@typescript-eslint/eslint-plugin
在根目录下建立 eslint 配置文件:.eslintrc.js
module.exports={parser:'vue-eslint-parser',parserOptions:{parser:'@typescript-eslint/parser',ecmaVersion:,sourceType:'module',ecmaFeatures:{jsx:true}},extends:['plugin:vue/vue3-recommended','plugin:@typescript-eslint/recommended','prettier/@typescript-eslint','plugin:prettier/recommended'],rules:{'@typescript-eslint/ban-ts-ignore':'off','@typescript-eslint/explicit-function-return-type':'off','@typescript-eslint/no-explicit-any':'off','@typescript-eslint/no-var-requires':'off','@typescript-eslint/no-empty-function':'off','vue/custom-event-name-casing':'off','no-use-before-define':'off',//'no-use-before-define':[//'error',//{//functions:false,//classes:true,//},//],'@typescript-eslint/no-use-before-define':'off',//'@typescript-eslint/no-use-before-define':[//'error',//{//functions:false,//classes:true,//},//],'@typescript-eslint/ban-ts-comment':'off','@typescript-eslint/ban-types':'off','@typescript-eslint/no-non-null-assertion':'off','@typescript-eslint/explicit-module-boundary-types':'off','@typescript-eslint/no-unused-vars':['error',{argsIgnorePattern:'^h$',varsIgnorePattern:'^h$'}],'no-unused-vars':['error',{argsIgnorePattern:'^h$',varsIgnorePattern:'^h$'}],'space-before-function-paren':'off',quotes:['error','single'],'comma-dangle':['error','never']}};
建立 prettier.config.js
module.exports={printWidth:100,tabWidth:2,useTabs:false,semi:false,//未尾逗号vueIndentScriptAndStyle:true,singleQuote:true,//单引号quoteProps:'as-needed',bracketSpacing:true,trailingComma:'none',//未尾分号jsxBracketSameLine:false,jsxSingleQuote:false,arrowParens:'always',insertPragma:false,requirePragma:false,proseWrap:'never',htmlWhitespaceSensitivity:'strict',endOfLine:'lf'}
4. vue-router、vuex
npminstallvue-router@4vuex
4.1 vuex
在根目录下创建 store/index.ts
import{InjectionKey}from'vue'import{createStore,Store}from'vuex'exportinterfaceState{count:number}exportconstkey:InjectionKey<Store<State>>=Symbol()exportconststore=createStore<State>({state(){return{count:0}},mutations:{increment(state){state.count++}}})
main.ts 修改
import{createApp}from'vue'import{store,key}from'./store'importAppfrom'./App'import'./index.css'constapp=createApp(App)app.use(store,key)app.mount('#app')
components/HelloWord.vue 修改
<template><h1>{{msg}}</h1><button@click="inCrement">countis:</button><p>{{count}}</p></template><script>import{defineComponent,computed}from'vue'import{useStore}from'vuex'import{key}from'../store'exportdefaultdefineComponent({name:'HelloWorld',props:{msg:{type:String,default:''}},setup(){conststore=useStore(key)constcount=computed(()=>store.state.count)return{count,inCrement:()=>mit('increment')}}})</script>
4.2 vue-router
在 src 目录下建立 router/index.ts,内容如下:
import{createRouter,createWebHistory,RouteRecordRaw}from"vue-router";importHelloWorldfrom"../components/HelloWorld.vue";constroutes:Array<RouteRecordRaw>=[{path:"/",name:"HelloWorld",component:HelloWorld,},{path:"/about",name:"About",//routelevelcode-splitting//thisgeneratesaseparatechunk(about.[hash].js)forthisroute//whichislazy-ponent:()=>import(/*webpackChunkName:"About"*/"../components/About.vue")}];constrouter=createRouter({history:createWebHistory(process.env.BASE_URL),routes,});exportdefaultrouter;
再新建一个 components/About.vue 文件,内容如下:
<template><imgalt="Vuelogo"src="../assets/logo.png"/><h1>{{msg}}</h1></template><scriptlang="ts">import{defineComponent}from'vue'exportdefaultdefineComponent({name:'About',data(){return{msg:'HelloVue3.0+Vite!'}},setup(){}})</script>
再修改 main.ts
import{createApp}from'vue'import{store,key}from'./store'importrouterfrom"./router";importAppfrom'./App'import'./index.css'constapp=createApp(App)app.use(store,key)app.use(router)app.mount('#app')
再访问 http://localhost:3000/
和 http://localhost:3000/about 即可
5. 加入 Element Plus
5.1 安装 element-plus
全局安装
npminstallelement-plus--save
5.2 引入 Element Plus
你可以引入整个 Element Plus,或是根据需要仅引入部分组件。我们先介绍如何引入完整的 Element。
完整引入
在 main.js 中写入以下内容:
import{createApp}from'vue'importElementPlusfrom'element-plus';importrouterfrom"./router";import'element-plus/lib/theme-chalk/index.css';importAppfrom'./App.vue';import'./index.css'constapp=createApp(App)app.use(ElementPlus)app.use(router)app.mount('#app')
以上代码便完成了 Element Plus 的引入。需要注意的是,样式文件需要单独引入。
按需引入
借助babel-plugin-component,我们可以只引入需要的组件,以达到减小项目体积的目的。
首先,安装 babel-plugin-component:
npminstallbabel-plugin-component-D
然后,将 .babelrc 修改为:
{"plugins":[["component",{"libraryName":"element-plus","styleLibraryName":"theme-chalk"}]]}
接下来,如果你只希望引入部分组件,比如 Button 和 Select,那么需要在 main.js 中写入以下内容:
import{createApp}from'vue'import{store,key}from'./store';importrouterfrom"./router";import{ElButton,ElSelect}from'element-plus';importAppfrom'./App.vue';import'./index.css'constapp=createApp(App)ponent(ElButton.name,ElButton);ponent(ElSelect.name,ElSelect);/*or*app.use(ElButton)*app.use(ElSelect)*/app.use(store,key)app.use(router)app.mount('#app')app.mount('#app')
更详细的安装方法请看 快速上手。
5.3 全局配置
在引入 Element Plus 时,可以传入一个全局配置对象。
该对象目前支持size
与zIndex
字段。size
用于改变组件的默认尺寸,zIndex
设置弹框的初始 z-index(默认值:2000)。按照引入 Element Plus 的方式,具体操作如下:
完整引入 Element:
import{createApp}from'vue'importElementPlusfrom'element-plus';importAppfrom'./App.vue';constapp=createApp(App)app.use(ElementPlus,{size:'small',zIndex:3000});
按需引入 Element:
import{createApp}from'vue'import{ElButton}from'element-plus';importAppfrom'./App.vue';constapp=createApp(App)app.config.globalProperties.$ELEMENT=optionapp.use(ElButton);
按照以上设置,项目中所有拥有size
属性的组件的默认尺寸均为 'small',弹框的初始 z-index 为 3000。
5.4 配置 vite.config.ts
其中 proxy 和 alias 是和 vue-cli 区别比较大的地方。
import{defineConfig}from'vite'importvuefrom'@vitejs/plugin-vue'importstyleImportfrom'vite-plugin-style-import'importpathfrom'path'//https://vitejs.dev/config/exportdefaultdefineConfig({plugins:[vue(),styleImport({libs:[{libraryName:'element-plus',esModule:true,ensureStyleFile:true,resolveStyle:(name)=>{return`element-plus/lib/theme-chalk/${name}.css`;},resolveComponent:(name)=>{return`element-plus/lib/${name}`;},}]})],/***在生产中服务时的基本公共路径。*@default'/'*/base:'./',/***与“根”相关的目录,构建输出将放在其中。如果目录存在,它将在构建之前被删除。*@default'dist'*///outDir:'dist',server:{//hostname:'0.0.0.0',host:"localhost",port:3001,////是否自动在浏览器打开//open:true,////是否开启https//https:false,////服务端渲染//ssr:false,proxy:{'/api':{target:'http://localhost:3333/',changeOrigin:true,ws:true,rewrite:(pathStr)=>pathStr.replace('/api','')},},},resolve:{//导入文件夹别名alias:{'@':path.resolve(__dirname,'./src'),views:path.resolve(__dirname,'./src/views'),components:path.resolve(__dirname,'./src/components'),utils:path.resolve(__dirname,'./src/utils'),less:path.resolve(__dirname,"./src/less"),assets:path.resolve(__dirname,"./src/assets"),com:path.resolve(__dirname,"./src/components"),store:path.resolve(__dirname,"./src/store"),mixins:path.resolve(__dirname,"./src/mixins")},}})
踩到坑
在npm run dev
打包时不报错,但是在npm run build
时却报错了,build 的时候会把node_modules
里面的文件也编译,所以挺多 element-plus 的类型文件报错了。
把tsconfig.json
里面的include
和exclude
修改一下就不会了,配置如下
{"compilerOptions":{"target":"esnext","module":"esnext","moduleResolution":"node","strict":true,"jsx":"preserve","sourceMap":true,//忽略this的类型检查,Raiseerroronthisexpressionswithanimpliedanytype."noImplicitThis":false,"resolveJsonModule":true,"esModuleInterop":true,"lib":["esnext","dom"],"types":["vite/client"]},"include":["/src/**/*.ts","/src/**/*.d.ts","/src/**/*.tsx","/src/**/*.vue"],//ts排除的文件"exclude":["node_modules"]}
Vue3 + vite2 打包出来的文件和原来 vue2 版的差别也挺大的,由原来 2.5M 直接变成了 1.8M ,amazing!
最后
项目代码大多都是 2 年前的,还有很多可以优化的地方,这次重构的过程没对原来的样式和代码做什么改动,没那么多时间,加上我懒 ????
这次就升级了主要框架与相应的 ui 库,过了一遍 Vue3 中的 API,发现很多 Vue3 中新的 API 都用不上,主要是要熟练一下 Vue3 和 Vite2 项目搭建,这假期也算有所收获。
具体项目源码请看:
/biaochenxuying/blog-vue-typescript
至此,一个基于 Vue3 全家桶 + Vite2 + TypeScript + Element Plus 的开发环境已经搭建完毕,现在就可以编写代码了,各个组件的使用方法请参阅它们各自的文档。
不得不说 Vue3 + Element Plus + Vite + TypeScript 是真的香!
推荐一个 Vue3 相关的资料汇总:Vue3 的学习教程汇总、源码解释项目、支持的 UI 组件库、优质实战项目,相信你会挖到矿哦!
推荐阅读
TypeScript 中提升幸福感的 10 个高级技巧