前言
本文記錄Vue2.x + Element-UI + TypeScript語法入門實例
為什么要用TypeScript?
1、TypeScript是JavaScript的超集,利用es6語法,實現對js的面向對象編程思想;
2、TypeScript會像強類型語言一樣,可以避免出現不可預期的運行時bug;
Vue官網:https://cn.vuejs.org/
Element-UI官網:https://element.eleme.cn/#/zh-CN
2021-03-03更新:
element-ui已經升級適配vue3,並全新發布新項目:Element Plus
官網:https://element-plus.gitee.io/#/zh-CN
GitHub:https://github.com/element-plus/element-plus
Vue對TypeScript的支持:https://cn.vuejs.org/v2/guide/typescript.html
TypeScript的學習可以看回我們之前的《typescript》系列:https://www.cnblogs.com/huanzi-qch/category/1509796.html
vue+typescript整合,推薦閱讀這篇文章:https://segmentfault.com/a/1190000011744210
安裝、啟動
vue項目需要node環境,開始安裝之前,先安裝node環境,安裝node環境看之前的博客:TypeScript環境安裝,以及配置idea開發環境
裝好node環境之后,先安裝Vue CLI手腳架,通過手腳架來快速創建項目
npm install -g @vue/cli
裝好之后使用命令查看版本號,確認一下
vue --version
裝好了手腳架,然后就可以通過手腳架快速創建portal門戶項目
PS:element-ui現在還不支持Vue3,所以我們現在Vue2就可以了,選擇自定義安裝把配置項全都勾上,或者可以先選擇默認安裝
vue create portal
后續再在package.json里面指定依賴包,直接使用IDE(或者手敲命令也一樣),運行安裝命令下載依賴包
package.json依賴配置
"dependencies": { "core-js": "^3.6.5", "register-service-worker": "^1.7.1", "vue": "^2.6.11", "vue-class-component": "^7.2.3", "vue-property-decorator": "^8.4.2", "vue-router": "^3.2.0", "vuex": "^3.4.0", "axios": "0.21.0", "element-ui": "^2.13.2", "js-cookie": "2.2.1" },
在src的同級目錄下面,創建vue.config.js配置文件,配置端口等
module.exports = { publicPath: './', outputDir: 'dist', assetsDir: 'static', lintOnSave: true, productionSourceMap: false, devServer: { port: '10010', open: false, overlay: { warnings: false, errors: true }, proxy: { '/auth': { target: 'http://localhost:10086', secure: false, changeOrigin: true, pathRewrite: { '^/auth': '/' } }, '/api': { target: 'http://localhost:10086', secure: false, changeOrigin: true, pathRewrite: { '^/api': '/' } } } } }
使用idea打開項目,配置config,即可在idea運行vue項目
或者也可以手動輸入命令啟動項目(vue-cli-service serve),或者在package.json文件中運行腳本
啟動成功
項目結構
Vue Router
Vue Router官網:https://router.vuejs.org/zh/
路由配置
import Vue from 'vue' import VueRouter, { RouteConfig } from 'vue-router' Vue.use(VueRouter); /* 公用模塊菜單路由 */ const commonRoutes: Array<RouteConfig> = [ { path: '/', name: 'Home', meta: { title: '主頁' }, component: () => import( '@/views/Home.vue') }, { path: '/404', name: '404', meta: { title: '404' }, component: () => import('@/views/common/404.vue') }, { path: '*', redirect: '/404'} ]; /* test模塊菜單路由 */ const testRoutes: Array<RouteConfig> = [ { path: '/test', name: 'Test', meta: { title: 'demo測試' }, component: () => import( '@/views/test/Test.vue') } ]; const router = new VueRouter({ base:"/", mode: 'hash',//history hash routes:commonRoutes.concat(testRoutes) }); router.beforeEach(async(to, from, next) => { console.log("跳轉開始,目標:"+to.path); document.title = `${to.meta.title}`; //跳轉頁面 next(); }); router.afterEach(() => { console.log("跳轉結束"); }); export default router
路由跳轉、頁面接參
/** * 工具類 */ export default class CommonUtil { /** * 從url中獲取參數 * 實例:http://xxxxx/index?id=1&name=張三 * getQueryVariable("id")//1 * getQueryVariable("name")//張三 */ public static getQueryVariable(variable:string): string { let vars = window.location.search.substring(1).split("&"); for (let i = 0; i < vars.length; i++) { let pair = vars[i].split("="); if (pair[0] === variable) { return pair[1]; } } return ""; }; }
import CommonUtil from "@/utils/commonUtil" //跳轉 params 是路由的一部分,必須要有。query 是拼接在url后面的參數,沒有也沒關系 this.$router.push({name:'Home', params: {id:'001'}, query: {id:'001'} }); //接參 let id = this.$route.params.id; //如果為空,嘗試從url中獲取參數 if(!id){ id = CommonUtil.getQueryVariable("id"); }
或者直接在瀏覽器上輸入path路徑,即可跳轉頁面
Vuex
Vuex官網:https://vuex.vuejs.org/zh/
vuex配置
import Vue from 'vue' import Vuex from 'vuex' Vue.use(Vuex); /* 約定,組件不允許直接變更屬於 store 實例的 state,而應執行 action 來分發 (dispatch) 事件通知 store 去改變 */ export default new Vuex.Store({ state: { }, getters:{ }, mutations: { }, actions: { }, modules: { } })
項目入口
App.vue
<!-- 這里是項目路口,配置<router-view/>即可 --> <template> <div id="app"> <router-view/> </div> </template> <script lang="ts"> </script> <style lang="less"> html,body{ margin: 0 !important; padding: 0 !important; } </style>
main.ts
import Vue from 'vue' import App from './App.vue' import './registerServiceWorker' import router from './router' import store from './store' import ElementUI from 'element-ui' import 'element-ui/lib/theme-chalk/index.css' // @ts-ignore import locale from 'element-ui/lib/locale/lang/zh-CN' Vue.use(ElementUI, { locale }); Vue.config.productionTip = false; new Vue({ router, store, render: h => h(App) }).$mount('#app');
TypeScript語法
Vue對TypeScript的支持:https://cn.vuejs.org/v2/guide/typescript.html
vue+typescript整合,推薦閱讀這篇文章:https://segmentfault.com/a/1190000011744210
在使用typescript語法的過程中,我們使用官方維護的 vue-class-component 裝飾器,這里是它的文檔:https://class-component.vuejs.org/
這個就是它一個簡單的寫法
<template> <div> 簡單頁面 </div> </template> <script lang="ts"> import { Component, Vue } from 'vue-property-decorator'; @Component export default class Home extends Vue { } </script> <style scoped> </style>
完整測試例子
接下來介紹具體的使用,里面包含了常用的:data 數據、生命周期鈎子函數、methods 普通方法、computed 獲取/設置計算屬性、watch 監聽、props 組件數據傳遞
HelloWorld組件
<template> <div class="hello"> <h1>{{ msg }}</h1> </div> </template> <script lang="ts"> import { Component, Prop, Vue } from 'vue-property-decorator'; @Component export default class HelloWorld extends Vue { //props 組件數據傳遞 @Prop({ type: String,default: 'default value' }) private msg!: string; } </script> <style scoped lang="less"> </style>
Test.vue測試頁面
<template> <div style="padding: 20px;"> <el-row> <el-col :span="12"> <div style="padding: 20px;"> <el-divider content-position="left">數據綁定測試</el-divider> <el-row> <el-input placeholder="請輸入新msg內容" v-model="msg" clearable></el-input> <p>直接綁定data數據:{{msg}}</p> <p>普通方法獲取data數據:{{getMsg()}}</p> <p>computed的get方法獲取data數據:{{computedTest}}</p> <el-button type="primary" plain @click="buttonClick">調用computed的set方法修改data數據</el-button> </el-row> <el-divider content-position="left">引用HelloWorld組件測試</el-divider> <el-row> <HelloWorld :msg="msg"/> </el-row> <el-divider content-position="left">if-else條件渲染測試</el-divider> <el-row> <p style="color: green" v-if="flag">if條件渲染:true</p> <p style="color: red" v-else="flag">if條件渲染:false</p> <el-button type="primary" plain @click="flag=!flag">if條件渲染取反</el-button> </el-row> </div> </el-col> <el-col :span="12"> <div style="padding: 20px;"> <el-divider content-position="left">for循環-數組渲染測試</el-divider> <el-row> <p v-for="(item,index) in items"> 序號:{{index}},編號:{{item.id}},姓名:{{item.name}} </p> <el-button type="primary" plain @click="items.push({id:'0000',name:'new item'})">新增記錄</el-button> </el-row> <el-divider content-position="left">for循環-對象渲染測試</el-divider> <el-row> <p v-for="(value,key,index) in itemsByObj"> 序號:{{index}},{{key}}:{{value}} </p> </el-row> </div> </el-col> </el-row> </div> </template> <script lang="ts"> import { Component, Emit, Watch, Prop, Vue } from 'vue-property-decorator'; import HelloWorld from '@/components/HelloWorld.vue'; @Component({ components: { HelloWorld, }, }) export default class Test extends Vue { //data 數據 private msg:string = "test測試"; private flag:boolean = true; private items:any = [ {id:1001,name:"張三"}, {id:1002,name:"李四"}, {id:1002,name:"王五"}, ]; private itemsByObj:object = { id:1001, name:"huanzi-qch", age:18, email:"huanzi-qch@qq.com", phone:"12345678900", }; //生命周期鈎子函數 created(){ console.log("created"); }; mounted(){ console.log("mounted"); }; //methods 普通方法 @Emit() getMsg(): string{ return this.msg; } @Emit() buttonClick(): void{ this.computedTest = 'test測試0001'; } //computed 獲取/設置計算屬性 get computedTest(): string{ return this.msg; } set computedTest(newMsg:string){ this.msg = newMsg; } //watch 監聽 @Watch('msg') onMsgChanged(newVal: string, oldVal: string) { this.$message.info("msg值發生改變,舊值:" + oldVal + ",新值:" + newVal); } } </script> <style scoped> </style>
效果演示
路由配置哪里要注意
//history:路徑直接是/test,部署到Tomcat后不能直接訪問地址欄 //hash:路徑會多一層/#/test,部署到Tomcat后能直接訪問地址欄 mode: 'hash',
環境配置文件
配置文件的key,要以VUE_APP_開頭
讀取
<template> <div> {{adminUrl}} </div> </template> <script lang="ts"> import { Component, Vue } from 'vue-property-decorator'; @Component export default class Home extends Vue { //讀取環境配置值 private adminUrl:string = process.env.VUE_APP_ADMIN_URL; } </script> <style scoped> </style>
favicon.ico圖標
通常情況下,我們是這樣設置自己的favicon.ico
在項目HTML頁面中,引入我們的favicon圖片
但是不管是在運行之后的頁面,還是打包之后的頁面,這行代碼都是被注釋起來的
我們可以再打包生成的index頁面中,把這個注釋放開,這樣就可以正常顯示我們的favicon圖片了
同時、我們可以再vue.config.js中進行設置
pwa配置項說明:https://cli.vuejs.org/zh/config/#pwa
效果
打包、部署
vue.conifg.js中指定好打包路徑
publicPath: './', outputDir: 'dist', assetsDir: 'static',
同時,路由配置那里要注意,模式要改成mode: 'hash'
const router = new VueRouter({ base:"/", mode: 'hash',//history hash routes:commonRoutes.concat(testRoutes,adminRoutes) });
直接運行打包腳本,或者手動輸入命令
打包成功
復制dist文件夾到Tomcat容器,把Tomcat運行起來
訪問:http://172.16.35.52:10086/dist/,即可跳轉到我們配置的 / 路徑頁面
在此路徑基礎上就可以訪問我們配置的路由路徑了,如:http://172.16.35.52:10086/dist/#/test
2020-11-20更新
路由的配置要注意
//history:路徑直接是/test,文件丟到Tomcat的webapps,文件夾名 + url路徑不能訪問(需要把文件放在ROOT默認文件夾下面) //hash:路徑會多一層/#,/#/test,文件丟到Tomcat的webapps,文件夾名 + url路徑能訪問 const router = new VueRouter({ base:"/portal", mode: 'history', routes:commonRoutes.concat(testRoutes) });
同時要注意,我們在vue.conifg.js中配置的服務信息,依賴node環境,如果是Tomcat,沒有代理功能,需要配合nginx上配置代理地址
devServer: { port: '10010', open: false, overlay: { warnings: false, errors: true }, proxy: { '/auth': { target: 'http://localhost:10086', secure: false, changeOrigin: true, pathRewrite: { '^/auth': '/' } }, '/api': { target: 'http://localhost:10086', secure: false, changeOrigin: true, pathRewrite: { '^/api': '/' } } } }
后記
Vue項目入門實例就暫時記錄到這,后續再繼續更新
代碼開源
注:portal前端就是本文中的vue項目
代碼已經開源、托管到我的GitHub、碼雲: