推荐一个 Vue3 全家桶 + TS+ Vite2 + element-plus 的网站实战项目
五一期間,花了 3 天時間,邊學 Vue3 和 Vite2,邊重構自己的項目,終于都用 Vue3 + TypeScript + Vite2 + Vuex4 + Vue-Router4 + element-plus 重構完啦!
終于完成一項心心念念的 2021 年度目標了 ??
項目地址:
https://github.com/biaochenxuying/blog-vue-typescript
效果
效果圖:
pc 端
移動端
完整效果請看:
https://biaochenxuying.cn
功能
已經完成功能
[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 創建項目
yarn?create?vite-app?<project-name>#?或者 npm?init?vite-app?<project-name>然后按照提示操作即可!
進入項目,安裝依賴
cd?<project-name>yarn?#?或?npm?i運行項目
yarn?dev?打開瀏覽器 http://localhost:3000 查看
2. 引入 TypeScript
在創建項目的時候可以 TypeScript 的,如果你選擇了 TypeScript ,可以忽略第 2 個步驟。
加入 ts 依賴
yarn?add?--dev?typescript在 項目根目錄下創建 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?*/ import?type?{?DefineComponent?}?from?'vue'declare?module?'*.vue'?{const?component:?DefineComponent<{},?{},?any>export?default?component }把 main.js 修改成 main.ts
在根目錄,打開 Index.html
<script?type="module"?src="/src/main.js"></script> 修改為: <script?type="module"?src="/src/main.ts"></script>3. 引入 eslint
安裝 eslint prettier 依賴
@typescript-eslint/parser @typescr ipt-eslint/eslint-plugin 為 eslint 對 typescript 支持。
yarn?add?--dev?eslint?prettier?eslint-config-prettier?eslint-plugin-prettier?eslint-plugin-vue?@typescript-eslint/parser?@typescr?ipt-eslint/eslint-plugin在根目錄下建立 eslint 配置文件:.eslintrc.js
module.exports?=?{parser:?'vue-eslint-parser',parserOptions:?{parser:?'@typescript-eslint/parser',ecmaVersion:?2020,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
npm?install?vue-router@4?vuex4.1 vuex
在根目錄下創建 store/index.ts
import?{?InjectionKey?}?from?'vue' import?{?createStore,?Store?}?from?'vuex'export?interface?State?{count:?number }export?const?key:?InjectionKey<Store<State>>?=?Symbol()export?const?store?=?createStore<State>({state()?{return?{count:?0}},mutations:?{increment(state)?{state.count++}} })main.ts 修改
import?{?createApp?}?from?'vue' import?{?store,?key?}?from?'./store' import?App?from?'./App' import?'./index.css'const?app?=?createApp(App)app.use(store,?key)app.mount('#app')components/HelloWord.vue 修改
<template><h1>{{?msg?}}</h1><button?@click="inCrement">?count?is:?</button><p>{{?count?}}</p> </template><script>import?{?defineComponent,?computed?}?from?'vue'import?{?useStore?}?from?'vuex'import?{?key?}?from?'../store'export?default?defineComponent({name:?'HelloWorld',props:?{msg:?{type:?String,default:?''}},setup()?{const?store?=?useStore(key)const?count?=?computed(()?=>?store.state.count)return?{count,inCrement:?()?=>?store.commit('increment')}}}) </script>4.2 vue-router
在 src 目錄下建立 router/index.ts,內容如下:
import?{?createRouter,?createWebHistory,?RouteRecordRaw?}?from?"vue-router"; import?HelloWorld?from?"../components/HelloWorld.vue";const?routes:?Array<RouteRecordRaw>?=?[{path:?"/",name:?"HelloWorld",component:?HelloWorld,},{path:?"/about",name:?"About",//?route?level?code-splitting//?this?generates?a?separate?chunk?(about.[hash].js)?for?this?route//?which?is?lazy-loaded?when?the?route?is?visited.component:?()?=>import(/*?webpackChunkName:?"About"?*/?"../components/About.vue")} ];const?router?=?createRouter({history:?createWebHistory(process.env.BASE_URL),routes, });export?default?router;再新建一個 components/About.vue 文件,內容如下:
<template><imgalt="Vue?logo"src="../assets/logo.png"/><h1>{{?msg?}}</h1> </template><script?lang="ts"> import?{?defineComponent?}?from?'vue'export?default?defineComponent({name:?'About',data()?{return?{msg:?'Hello?Vue?3.0?+?Vite!'}},setup()?{} }) </script>再修改 main.ts
import?{?createApp?}?from?'vue' import?{?store,?key?}?from?'./store' import?router?from?"./router"; import?App?from?'./App' import?'./index.css'const?app?=?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
全局安裝
npm?install?element-plus?--save5.2 引入 Element Plus
你可以引入整個 Element Plus,或是根據需要僅引入部分組件。我們先介紹如何引入完整的 Element。
完整引入
在 main.js 中寫入以下內容:
import?{?createApp?}?from?'vue' import?ElementPlus?from?'element-plus'; import?router?from?"./router"; import?'element-plus/lib/theme-chalk/index.css'; import?App?from?'./App.vue'; import?'./index.css'const?app?=?createApp(App) app.use(ElementPlus) app.use(router) app.mount('#app')以上代碼便完成了 Element Plus 的引入。需要注意的是,樣式文件需要單獨引入。
按需引入
借助?babel-plugin-component,我們可以只引入需要的組件,以達到減小項目體積的目的。
首先,安裝 babel-plugin-component:
npm?install?babel-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'; import?router?from?"./router"; import?{?ElButton,?ElSelect?}?from?'element-plus'; import?App?from?'./App.vue'; import?'./index.css'const?app?=?createApp(App) app.component(ElButton.name,?ElButton); app.component(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' import?ElementPlus?from?'element-plus'; import?App?from?'./App.vue';const?app?=?createApp(App) app.use(ElementPlus,?{?size:?'small',?zIndex:?3000?});按需引入 Element:
import?{?createApp?}?from?'vue' import?{?ElButton?}?from?'element-plus'; import?App?from?'./App.vue';const?app?=?createApp(App) app.config.globalProperties.$ELEMENT?=?option app.use(ElButton);按照以上設置,項目中所有擁有?size?屬性的組件的默認尺寸均為 'small',彈框的初始 z-index 為 3000。
5.4 配置 vite.config.ts
其中 proxy 和 alias 是和 vue-cli 區別比較大的地方。
import?{?defineConfig?}?from?'vite' import?vue?from?'@vitejs/plugin-vue' import?styleImport?from?'vite-plugin-style-import' import?path?from?'path'//?https://vitejs.dev/config/ export?default?defineConfig({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?的類型檢查,?Raise?error?on?this?expressions?with?an?implied?any?type."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 項目搭建,這假期也算有所收獲。
具體項目源碼請看:
https://github.com/biaochenxuying/blog-vue-typescript
至此,一個基于 Vue3 全家桶 + Vite2 + TypeScript + Element Plus 的開發環境已經搭建完畢,現在就可以編寫代碼了,各個組件的使用方法請參閱它們各自的文檔。
不得不說 Vue3 + Element Plus + Vite + TypeScript 是真的香!
推薦一個 Vue3 相關的資料匯總:Vue3 的學習教程匯總、源碼解釋項目、支持的 UI 組件庫、優質實戰項目,相信你會挖到礦哦!
推薦閱讀
TypeScript 中提升幸福感的 10 個高級技巧
總結
以上是生活随笔為你收集整理的推荐一个 Vue3 全家桶 + TS+ Vite2 + element-plus 的网站实战项目的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 前端月趋势榜:4 月最热门的 20 个前
- 下一篇: 推荐 12 个提升程序员软技能与效率的必