文章目錄
-
-
- 原創内容,轉載請注明出處^_^
- 基礎知識
-
- 為什麼要學習Vuejs
- 前後端分離暨Vuejs的優勢
-
- 變革
- 開發
- 運維
- 對比
- 什麼是MVVM?MVVM是Model-View-ViewModel的縮寫
- 檔案
- 配置
-
- 啟動配置
- 元件
-
- 概念
-
- package.json.lock
- vue-cli是vue.js的腳手架,用于自動生成vue.js+webpack的項目模闆
- export default
- $refs
- 定義元件
- 注冊元件
- 生命周期
-
- computed
- 文法
-
- Object.assign
- 工具
-
- 同步
-
- Promise.all
- Promise.race
- 建構類
- VueX
-
- mutations
- rules
- Element-ui
-
- 穿梭框踩坑
- 踩坑
-
- 錯誤記錄
-
- 簡單
-
- 代碼不起效
- Error in created hook: "TypeError: _admin.menuTree.then is not a function"
- 請求頭類型不對
- 'vue-cli-service' 不是内部或外部指令,也不是可運作的程式
- npm相關
- Webpack相關
- 簡易功能開發
-
- 取消表單驗證提示
- ajax
- 登入
-
- 邏輯
- 源碼解析
-
- 背景API切換回Mock需改動
- Token - 令牌
- 全局變量
- 引入模闆
-
- 彈窗
- 編輯器
-
- Json編輯器
- 布局
-
- 九宮格|十六宮格
- 相容
-
- 解決IE相容問題
- 特殊代碼
-
- cookie
-
版本号 | 作者 | 備注 | |
---|---|---|---|
v1.0.2 | 飛豺 | 8416837 | Vue 2.6.10 |
原創内容,轉載請注明出處_
基礎知識
為什麼要學習Vuejs
-
網友:"到今天這個時代有些人學完了js、html5/dom/bom直接跳過jQ去學vue我覺得完全沒問題,是以不懂jQ的人會vue當然可以,本來前端的基礎就是js而不是jQ。而且vue本身走的就是子產品化的開發方式,就是讓項目更好維護,更好更新,在這些方面絕對比jQ更優秀。jQuery之是以被替代,一是開發方式保守、老舊、效率低,二是因為html5出了很多新的api,完全可以代替jQ,就連bootstrap重構都已經申明将剔除jQ。退一萬步說,前端最基礎的還是js,隻要你js技術過關,不管是學jQ還是vue都會很快。"
-
vue.js的作者尤雨溪是中國人,在知乎上有帳号,且非常活躍。
尤雨溪畢業于上海複旦附中,在美國完成大學學業,大學畢業于Colgate University,後在Parsons設計學院獲得Design & Technology藝術碩士學位,現就職于紐約Google Creative Lab。
2016年9月3日,南京JSConf,尤雨溪宣布加盟阿裡巴巴Weex團隊,尤雨溪稱他将以技術顧問的身份加入Weex 團隊來做 Vue 和 Weex 的 JavaScript runtime 整合,目标是Vue跨三端。
前後端分離暨Vuejs的優勢
變革
- 解耦,視圖-網關-服務各司其職。自由組合搭配。
- 後端無狀态,不存儲使用者資訊,不維護session減輕負擔。安全提升;
- 單頁模式,簡化握手,響應更快;
- 靜态頁面部署到nginx,響應更快,且更新時無感覺,不需重新開機;
- Reactive程式設計。響應式,資料改變時,頁面實時響應。
- 高效,将某些運算遷移到前端,類似邊緣計算-分攤中心壓力;
- 簡化代碼,後端API機械式自動生成,前端調用API即可;
- 技術健全,如路由、存儲、生命周期管控、監聽、計算、調試、測試架構,規範成熟;
- 漸進式,自然融入到已經上線的項目,持續增加需求,擴充性強;
- 資料渲染高效;
- 優秀案例;
- 技術革新、推廣、傳承;
開發
- 上手較快,因為基礎還是JS;
- 熱部署,代碼更新後即時生效而無需建構。傳統開發亦可以通過一定配置進行熱部署,但有時會失效。
- 靈活與API|MQ互動;
- 相容技術棧,如WebSocket、MQ等;
- 豐富的元件庫,提升效率;
- 調試、測試架構;
- 脫離後端,暫無API時,可使用mock.js架構模拟;
- 文法糖有助于實作複雜功能;
- 專用IDE,如webstorm;
- 文檔詳盡易懂,社群活躍壯大中;
- 開發體驗:規範、生命周期控制,資料綁定,熱部署不需複雜設定,JS與DOM解耦,不用直接操作DOM,代碼複用,MVVM結構,面向對象-後端思維;
運維
- 獨立部署,減輕後端壓力,釋出以及服務崩潰互不影響;
- 松散耦合,定位BUG快捷;
- 代碼易讀,易維護,新增需求無壓力,如多用戶端需求、前後端分離需求;
- 建構靜态檔案,防止開發技術外洩;
對比
- Thymeleaf、JSP與DOM耦合,前者遵循xml規範,模闆隻解決了渲染,沒解決麻煩的DOM問題;
什麼是MVVM?MVVM是Model-View-ViewModel的縮寫
要編寫可維護的前端代碼絕非易事。我們已經用MVC模式通過koa實作了後端資料、模闆頁面和控制器的分離,但是,對于前端來說,還不夠。
這裡有童鞋會問,不是講Node後端開發嗎?怎麼又回到前端開發了?
對于一個全棧開發工程師來說,懂前端才會開發出更好的後端程式(不懂前端的後端工程師會設計出非常難用的API),懂後端才會開發出更好的前端程式。程式設計的基本思想在前後端都是通用的,兩者并無本質的差別。這和“不想當廚子的裁縫不是好司機”是一個道理。
改變JavaScript對象的狀态,會導緻DOM結構作出對應的變化!這讓我們的關注點從如何操作DOM變成了如何更新JavaScript對象的狀态,而
操作JavaScript對象比DOM簡單
多了!
這就是MVVM的設計思想:
關注Model的變化,讓MVVM架構去自動更新DOM的狀态,進而把開發者從操作DOM的繁瑣步驟中解脫出來
! ——廖雪峰
ps:jQuery MVVM架構如JSViews
檔案
-
Vue中index.html、main.js、App.vue、index.js之前的關系以及加載過程
App.vue中的router-view
<template>
<div id="app">
<p>就是一張da圖檔</p>
[外鍊圖檔轉存失敗(img-dlDOl0R2-1562114250786)(https://mp.csdn.net/mdeditor/assets/logo.png)]
<!--PS: router-view渲染路由資訊于此-->
<router-view/>
</div>
</template>
router/index.js定義了簡單的路由資訊
-
main.js
入口,加載元件到index.html
配置
啟動配置
package.json檔案配置:
- 啟動指令
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "nodemon -e js,graphql -x node -r dotenv/config ./src/index.js",
"debug": "nodemon -e js,graphql -x node --inspect -r dotenv/config ./src/index.js",
"lint": "eslint --ext .js src"
},
比如,執行
yarn start
則走
start
腳本
package-lock.json作用
元件
概念
package.json.lock
舉個例子:
“dependencies”: {
“@types/node”: “^8.0.33”,
},
這裡面的 向上标号^是定義了向後(新)相容依賴,指如果 types/node的版本是超過8.0.33,并在大版本号(8)上相同,就允許下載下傳最新版本的 types/node庫包,例如實際上可能運作npm install時候下載下傳的具體版本是8.0.35.
vue-cli是vue.js的腳手架,用于自動生成vue.js+webpack的項目模闆
- 元件類似自定義元素.Web元件規範
- 在一個大型應用中,有必要将整個應用程式劃分為元件,以使開發更易管理。假想例子,以便展示元件的結構↓
<div id="app">
<app-nav></app-nav>
<app-view>
<app-sidebar></app-sidebar>
<app-content></app-content>
</app-view>
</div>
-
const
常量
-
export
檔案通過export暴露接口|變量
- h => h(App)
// 演變步驟
render: function (createElement) {
return createElement(App);
}
render (createElement) {
return createElement(App);
}
render (h){
return h(App);
}
It comes from the term “hyperscript”, which is commonly used in many virtual-dom implementations. “Hyperscript” itself stands for “script that generates HTML structures” because HTML is the acronym for “hyper-text markup language”. – by 尤雨溪
export default
-
ES6的export
使用export指令定義了子產品的對外接口以後,其他JS檔案就可以通過import指令加載這個子產品(檔案),沒錯
$refs
持有所有被ref定義的元件
定義元件
- 在不使用.vue 單檔案時,我們是通過 Vue 構造函數建立一個 Vue 根執行個體來啟動vuejs 項目,Vue 構造函數接受一個對象,這個對象有一些配置屬性 el, data, component, template 等,進而對整個應用提供支援。
-
new Vue()
new Vue() 相當于一個構造函數,在入口檔案 main.js 構造根元件的同時,如果根元件還包含其它子元件,那麼 Vue 會通過引入的選項對象構造其對應的 Vue 執行個體,最終形成一棵
。元件樹
- export default
- 比較new Vue() & export default
- 全局元件
Vue.component('todo-item', {
template: '<li>這是個待辦項</li>'
})
-
.vue檔案
可以把html, css, js 寫到一個檔案中,進而實作了對一個元件的封裝
-
父子關系
在一個元件中通過 import 引入另一個元件,這個元件就是父元件,被引入的元件就是子元件.
父元件通過props 向子元件傳遞資料,子元件通過自定義事件向父元件傳遞資料.
注冊元件
- 全局注冊
Vue.component('component-a', { /* ... */ })
Vue.component('component-b', { /* ... */ })
Vue.component('component-c', { /* ... */ })
- 局部注冊
new Vue({
el: '#app',
components: {
'component-a': ComponentA,
'component-b': ComponentB
}
})
生命周期
computed
- 計算屬性,如将總價在computed裡計算,進而實時計算商品購物車裡的商品總價
- 對于任何複雜邏輯,你都應當使用計算屬性
- 無變化時,緩存,提高效率;
文法
Object.assign
參數帶上這個,參數會被對象化,如果參數是數字或字元串,會被拆分成一個個的數字或字元;
工具
同步
Promise.all
- 順序執行程序
// 假如有三個接口調用動作a1,a2,p5
Promise.all([a1,a2,p5]).then((result) => {
console.log(result) // 結果數組或者錯誤
}).catch((error) => {
console.log(error)
})
Promise.race
建構類
-
yarn
可以代替npm的包依賴管理工具
VueX
- 在SPA單頁面元件的開發中 Vue的vuex和React的Redux 都統稱為同一狀态管理,個人的了解是全局狀态管理更合适;簡單的了解就是你在state中定義了一個資料之後,你可以在所在項目中的任何一個元件裡進行擷取、進行修改,并且你的修改可以得到全局的響應變更.
- 管理Token、全局個人偏好
mutations
const storeLogin = new Vuex.Store({
state: {
// 存儲token
Authorization: localStorage.getItem('Authorization') ? localStorage.getItem('Authorization') : ''
},
mutations: {
// 修改token,并将token存入localStorage
changeLogin(state,user) {
console.log('進入changeLogin')
state.Authorization = user.Authorization;
localStorage.setItem('Authorization', user.Authorization);
}
}
});
又如↓
mutations: {
increment (state, payload) {
state.count += payload.amount
}
}
mutations下的函數隻适合接收一個對象參數,state是預設傳入,不能把state當做形參
rules
先在html引入rules
data裡定義rules
data() {
loginRules: {
// 驗證器 validateUsername 是一種特殊的函數
username: [{ required: true, trigger: 'blur', validator: validateUsername }],
password: [{ required: true, trigger: 'blur', validator: validatePassword }]
},
},
驗證器也在data裡面
const validateUsername = (rule, value, callback) => {
// validUsername 是引入的js函數
if (!validUsername(value)) {
callback(new Error('請輸入正确的使用者名'))
} else {
callback()
}
}
引入validUsername
Element-ui
// 引入ui
import ElementUI from 'element-ui' //element-ui的全部元件
import 'element-ui/lib/theme-chalk/index.css'//element-ui的css
Vue.use(ElementUI) //使用elementUI
cnpm install [email protected] -S
穿梭框踩坑
- 效果
Vuejs學習v1.0.3,持續更新中。。。
<div style="text-align: left">
<!-- <div style="text-align: center"> -->
<el-transfer
v-model="value4"
style="text-align: left; display: inline-block"
filterable
:render-content="renderFunc"
:titles="['使用者角色', '已選角色']"
:button-texts="['放棄', '選擇']"
:format="{
noChecked: '${total}',
hasChecked: '${checked}/${total}'
}"
:data="roleData"
:props="defaultProps"
@change="handleChange"
>
<!-- ↓等效:render-content-->
<!-- <span slot-scope="{ option }">{{ option.roleId }} - {{ option.roleName }}</span>-->
<el-button slot="left-footer" class="transfer-footer" size="small">操作</el-button>
<el-button slot="right-footer" class="transfer-footer" size="small">操作</el-button>
</el-transfer>
</div>
data() {
return {
// 角色資料
data: [],
// 别名
defaultProps: {
key: 'roleId',
label: 'roleName',
disabled: false
},
// 不要value4無法移動元素
value4: [1],
renderFunc(h, option) {
// 生成元素的顯示名稱
return <span>{ option.roleName }</span>
// return <span>{ option.roleId } - { option.roleName }</span>
},
- 目标框初始值不出來解決了
Vuejs學習v1.0.3,持續更新中。。。
踩坑
-
demo
如
cc.vue
# 建立cc元件
template>
<div>
中國工農紅軍
<ul>
<li v-for="site in sites" :key="site.name">
{{site.url}}
<a :href="site.url" target="_blank">{{site.name}}</a>
</li>
</ul>
<input type="button" value="點選我" @click="printText"/>
</div>
</template>
<script>
// import { METHODS } from 'http'
export default {
name: 'Cc',
methods: {
clickTest: function () {
alert('你點選了按鈕')
},
printText: function () {
console.log('你點選了按鈕')
}
},
data () {
return {
msg: '書籍是人類進步的階梯',
msg2: 'Apple',
sites: [
{url: 'http://router.vuejs.org/', name: 'Jack'},
{url: 'http://vuex.vuejs.org/', name: 'Tom'},
{url: 'https://github.com/vuejs/awesome-vue', name: 'Jimy'}
]
}
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
h1, h2 {
font-weight: normal;
}
ul {
list-style-type: none;
padding: 0;
}
li {
display: inline-block;
margin: 0 10px;
}
a {
color: #42b983;
}
</style>
index.js
# 注入元件
import cc from '@/components/cc'
錯誤記錄
簡單
代碼不起效
- 可能浏覽器緩存了,沒有更新,F12打開調試頁面,設定當打開F12時不緩存
Error in created hook: “TypeError: _admin.menuTree.then is not a function”
因為導入的函數沒加括弧
// 函數需要括弧,↓對的
menuTree().then(response => {
}
請求頭類型不對
// 元件内部增加下述代碼 - 局部請求頭
import Axios from 'axios'
Axios.defaults.headers.post['Content-Type'] = 'application/json'
# 或者在axios api裡加入
export function call(data) {
return request({
headers: {
'Content-Type': 'application/json' // 設定請求頭請求格式為JSON
},
url: url,
method: 'post',
data
})
}
‘vue-cli-service’ 不是内部或外部指令,也不是可運作的程式
檢查指令執行後,報錯:
'vue-cli-service' 不是内部或外部指令,也不是可運作的程式
解決辦法:将原
node_modules
重命名,重新執行
cnpm run dev
即可。是以
node_modules
最好是每個項目有一個個性化的,比如某些項目某些子產品必須
npm
安裝。
npm相關
- js記憶體溢出
npm install -g increase-memory-limit
# 進入項目檔案夾運作:
increase-memory-limit
Webpack相關
- 找不到子產品 Can’t find module
// cmpnt = () => import(`@/views${m_url}`)
cmpnt = (resolve) => require([`@/views${m_url}`], resolve)
// 上面的改成下面的
檢視原理
簡易功能開發
取消表單驗證提示
再次打開,紅字依然存在,這個紅字是表單的,清除辦法:
this.$nextTick(() => {
this.$refs.form.clearValidate() // 有效
})
ajax
- axios
# install axios
cnpm install axios --save-dev
# --save-dev以省掉手動修改package.json檔案的步驟
axios發送ajax請求
<script src="/js/axios.min.js"></script>
window.onload=function(){
new Vue({
el:'#app',
data:{
users:{
name:'',
age:''
}
},
methods:{
sendPsot(){
axios.post('post.php', {
name: this.users.name,
age: this.users.age,
})
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
}
}
});
}
-
跨域配置
後端亦可配置跨域,前後端不要都配
,proxyTable裡增加内容(config/index.js
)老式
proxyTable: {
// 解決跨域
'/tbapi':{
// target: "http://api.douban.com/v2",
target: "https://suggest.taobao.com",
changeOrigin:true,
pathRewrite:{
'^/tbapi':''
}
}
},
元件js
// 跨域
Axios.defaults.baseURL = '/tbapi'
Axios.defaults.headers.post['Content-Type'] = 'application/json'
mounted() {
//GET
this.$ajax({
method: 'get',
// tbapi會代替localhost
url: '/sug?code=utf-8&q=電冰箱',
// url: '/sug?code=utf-8&q=電冰箱&callback=cb',
}).then(response => {
// response包含config data等
var resData = response.data.result
iceBoxes = resData
resData.forEach(item => {
console.log(item[0])
console.log(item[1])
// console.log('資料序号'+i+'=='+item)
})
}).catch(function (err) {
console.log(err)
})
//POST
this.$ajax({
method: 'post',
url: '/sug?code=utf-8&q=iPhone',
// data: {
// code: 'utf-8',
// q: 'iPhone'
// }
}).then(response => {
// response包含config data等
var resData = response.data.result
resData.forEach(item => {
console.log(item[0])
console.log(item[1])
// console.log('資料序号'+i+'=='+item)
})
}).catch(function (err) {
console.log(err)
});
}
登入
邏輯
handleLogin() {
this.$refs.loginForm.validate(valid => {
if (valid) {
this.loading = true
// 也就是說,全局store對象擁有原生的dispatch方法,用于請求API
this.$store.dispatch('user/login', this.loginForm).then(() => {
this.$router.push({ path: this.redirect || '/' })
this.loading = false
}).catch(() => {
this.loading = false
})
} else {
console.log('error submit!!')
return false
}
})
}
源碼解析
上文的$store來自這裡
@/store/index.js
,片段↓
const store = new Vuex.Store({
modules,
getters
})
// 導出執行個體this的store屬性
export default store
store的dispatch,該單詞是發送的意思
axios的配置
// request即service-axios
import request from '@/utils/request'
// ↑request含有攔截器,url改為合适的baseUrl
export function login(data) {
console.log('axios執行個體==',request)
return request({
url: '/user/login',
method: 'post',
data
})
}
當index.vue裡的store.dispatch執行請求時,即會找到上面的login函數,由login函數發出調用請求,接着,我們看
request.js
裡的代碼
import axios from 'axios'
import { MessageBox, Message } from 'element-ui'
import store from '@/store'
import { getToken } from '@/utils/auth'
// global全局配置
import { baseUrl } from '@/utils/global'
// create an axios instance 沒錯,service就是axios執行個體,[email protected]/utils/request即注入service-axios執行個體
const service = axios.create({
baseURL: baseUrl, // url = base url + request url
// baseURL: process.env.VUE_APP_BASE_API, // url = base url + request url
// withCredentials: true, // send cookies when cross-domain requests
timeout: 5000 // request timeout
})
↑這是axios的配置,配置了url和逾時時間,當執行$store.dispatch時,即會加上baseUrl進行請求。
接收響應↓,也來自
request.js
response => {
const res = response.data
// if the custom code is not 20000, it is judged as an error.
if (res.code !== 20000) {
Message({
message: res.message || 'Error',
type: 'error',
duration: 5 * 1000
})
// 50008: Illegal token; 50012: Other clients logged in; 50014: Token expired;
if (res.code === 50008 || res.code === 50012 || res.code === 50014) {
// to re-login
MessageBox.confirm('You have been logged out, you can cancel to stay on this page, or log in again', 'Confirm logout', {
confirmButtonText: 'Re-Login',
cancelButtonText: 'Cancel',
type: 'warning'
}).then(() => {
store.dispatch('user/resetToken').then(() => {
location.reload()
})
})
}
return Promise.reject(new Error(res.message || 'Error'))
} else {
return res
}
},
背景API切換回Mock需改動
// 1 request.js
const service = axios.create({
// baseURL: baseUrl, // url = base url + request url
baseURL: process.env.VUE_APP_BASE_API, // url = base url + request url
// withCredentials: true, // send cookies when cross-domain requests
timeout: 5000 // request timeout
})
// 2 user.js
// params: data
data
// 3 user.js
// method: 'post',
method: 'get',
Token - 令牌
- vuex的…mapMutations([’
- 前後端屬于不同的域,導緻每次ajax請求伺服器都會當做新的使用者通路,導緻session丢失。當然也可以通過維護cookie來讓服務端辨識用戶端,如
axios.defaults.withCredentials=true;
- 每次請求被認為是新用戶端,産生新session問題,注意session的膨脹;
全局變量
引入模闆
彈窗
- 建立模闆Test.vue
- 使用
import TestMode from './Test' // 導入相對路徑的Test.vue
...
components: { TestMode }, // 注冊元件
test(row) { // 這裡是父元件
this.testPageVisible = true
this.$nextTick(() => {
this.$refs.testMode2.test(Object.assign({}, row)) // 調用子元件的函數 testMode2
})
},
methods: {
test() { // 這裡是Test.vue元件·················
this.dialogFormVisible = true
}
}
編輯器
Json編輯器
原版↓很糟糕
使用json元件解決,待續
布局
九宮格|十六宮格
事态緊急跨域用表格代替九宮格,不過不正規。
正規的待續
相容
解決IE相容問題
- promise
# 安裝es6-promise
npm install es6-promise --save-dev
- main.js中引入ES6的polyfill
import Es6Promise from 'es6-promise'
Es6Promise.polyfill();
上文安裝的
promise
隻是針對性的,要徹底相容IE還需要研究。
特殊代碼
cookie
# Vue官方的localStorage
Vue.ls.get(key)