目錄
vue打包路徑問題/build不在服務器中打開空白/cordova打開爲空白
vue打包路徑問題/build不在服務器中打開空白---->cli-3.0
緩存/keep-alive/對router使用keep-alive
加上position:absolute後,動畫執行過程中抖動。
動畫執行過程中,header(position: fixed)部分不見了,動畫結束後顯示。
全局自動導入scss的function、變量、mixin\scss自動導入
vue-cli打包出現代碼不分離,可以使用第二種形式\代碼分離的多種寫法
路由A進入下一級B,緩存A;路由B進入下一級C,緩存B;B回退到A不緩存B
FAQ
資源
vueApi https://cn.vuejs.org/v2/api/
vue指導 https://cn.vuejs.org/v2/guide/
devTools調試 https://github.com/vuejs/vue-devtools
router路由 https://router.vuejs.org/zh/
vuex https://vuex.vuejs.org/zh/
srr服務器渲染 https://ssr.vuejs.org/zh/
初始化工程2.x
//vue-cli是2.X版本
npm install -g vue-cli
vue init webpack
初始化工程3.x
//vue-cli是3.X版本
npm install -g @vue/cli
# OR
yarn global add @vue/cli
你還可以用這個命令來檢查其版本是否正確 (3.x):
vue --version
創建一個項目
vue create hello-world
拉取 2.x 模板 (舊版本)
npm install -g @vue/cli-init
# `vue init` 的運行效果將會跟 `[email protected]` 相同
vue init webpack my-project
工程目錄
目錄"store"爲Vuex相關的文件
ie兼容包
npm i -s 'babel-polyfill'
import "babel-polyfill";------>注意import位置儘量靠前,必須vuex前面
webpack打包img
//把圖片包含進來
import png1 from '@/assets/666320.png';
import png2 from '@/assets/6pro320-220.png';
import png3 from '@/assets/pc-320-220-mi8se.png';
import png4 from '@/assets/ds50.png';
import png5 from '@/assets/ds320_220.png';
//使用
{
shopname: "手機",
list:[
png4,
png5,
]
},
定義組件過濾器
filters: {
numFilter(value) {
// 截取當前數據到小數點後三位
let transformVal = Number(value).toFixed(3)
let realVal = transformVal.substring(0, transformVal.length - 1)
// num.toFixed(3)獲取的是字符串
return Number(realVal)
}
}
全局過濾器和加載
過濾器定義"./filter/custom.js"
function numFilter(value) {
// 截取當前數據到小數點後三位
let transformVal = Number(value).toFixed(3)
let realVal = transformVal.substring(0, transformVal.length - 1)
// num.toFixed(3)獲取的是字符串
return Number(realVal)
}
/**
*
* @param {*} Vue
* @param {*} custom
*/
function init(Vue){
Object.keys(this).forEach(key => {
if(key!=='init') {
Vue.filter(key, this[key]);
// console.log(key,custom[key])
}
})
}
export {numFilter ,init}
加載、使用"main.js"
import * as custom from './filters/custom'
custom.init(Vue);//加載過濾器
深度監聽/對象以及內部屬性的監聽
深度監聽
watch: {
obj: {
handler(newName, oldName) {
console.log('obj.a changed');
},
immediate: true,
deep: true
}
}
第一個handler:其值是一個回調函數。即監聽到變化時應該執行的函數。
第二個是deep:其值是true或false;確認是否深入監聽。(一般監聽時是不能監聽到對象屬性值的變化的,數組的值變化可以聽到。)
第三個是immediate:其值是true或false;確認是否以當前的初始值執行handler的函數。
改變數組的值
var target = {};
Object.assign(target,item,{checked:val})
this.$set(this.list, index, target);
常見事件監聽
@click | @change 當前元素失去焦點並且元素的內容發生改變而觸發此事件 | @blur | @focus
原生事件監聽
@click.native | @mouseenter.native | @dbclick.native 。。。mousedown mouseup mouseover mouseout
路由器簡單使用
./router/index.js
import Vue from 'vue'
import Router from 'vue-router'
// import HelloWorld from '@/components/HelloWorld'
import index from '@/views/index'
// import Code404 from '@/components/Code404'
Vue.use(Router)
export default new Router({
routes: [
{
path: '/',
// name: 'index',
component: index
},
{
path: '*',
// name: 'index',
redirect: '/'
// component: Code404
}
]
})
main.js
import router from './router'
//放入根vue
new Vue({
el: '#app',
router,
components: { App },
template: '<App/>'
})
嵌套路由的使用/二級頁問題
// router/index.js
import Index from '@/views/index'
import MainPage from '@/views/MainPage'
import My from '@/views/my'
import Order from '@/views/order'
import ClasstifyAll from '@/views/ClasstifyAll'
import Search from '@/views/search'
import url from '../global/url'
let nav = url.navigator;
export default new Router({
routes: [
{
path: '/',
// name: 'HelloWorld',
redirect:'/mainpage/index'
},
/**
* mainpage頁,主頁
*/
{
path: nav.mainPage.selfpath,
name: 'MainPage',
component: MainPage,
children:[
{
path: nav.mainPage.children.indexPath,
name: 'index',
component: Index
},
{
path: nav.mainPage.children.myPath,
name: 'my',
component: My
},
{
path: nav.mainPage.children.orderPath,
name: 'order',
component: Order
}
]
},
/**
* classtifyAll頁,全部分類頁
*/
{
path: nav.classtifyAll,
name: 'classtifyAll',
component: ClasstifyAll,
},
{
path: nav.searchPath,
name: 'Search',
component: Search,
}
]
})
主頁中的子分頁(index、my、order等)需要以子頁形式存在,二級頁是跟主頁mainpage同級的,這樣在主頁子分頁中點擊了二級頁鏈接,才能使得二級頁全屏。
組件定義.vue文件
<template>
<div class="root">
</div>
</template>
<script>
export default {
props:['itemObj'],
data() {
return {
maxNum:99,
};
},
methods:{
del(){
this.list.splice(this.index,1);
},
},
computed: {
totalPrice() {
return this.itemObj.num * this.itemObj.price;
}
},
watch:{
// checked:function(val){
// }
},
filters: {
}
};
</script>
<style scoped>
.root{
display: flex;
width: 1200px;
/* background-color: #234566; */
color: #000;
font-size: 12px;
padding: 10px;
border: 1px solid #ddd;
}
</style>
組件註冊
全局註冊
Vue.component('my-component-name', {
// ... 選項 ...
})
局部註冊
import ComponentA from '@/components/ComponentA.js'
//或者
var ComponentA = { /* ... */ }
var ComponentB = { /* ... */ }
var ComponentC = { /* ... */ }
new Vue({
el: '#app',
components: {
'component-a': ComponentA,
'component-b': ComponentB
}
})
import路徑中包含的 "@"
例如:"@/components/xxx.js" @在webpack中有定義,表示src目錄
組件prop雙向綁定
第一種方式/自定義事件
父組件中
<text-document
v-bind:title="doc.title"
v-on:update:title="doc.title = $event"
></text-document>
子組件中
this.$emit('update:title', newTitle)
第二種方式/.sync修飾符
父組件中
<XXX v-bind:showFlag.sync="showFlag" >
子組件中
this.$emit('update:showFlag', false);
關閉eslint代碼檢測
在config/index.js目錄下
// useEslint: true,
useEslint: false,
第三種方式:v-model
父組件 <aa class="abc" v-model='test' ></aa> 。
子組件
export default {
props: {
value: { // 必須要使用value,才能接受v-model綁定的值變化
default: '',
},
},
methods: {
fn2 () {
this.$emit('input', this.value+2)
// 這兒必須用input 發送數據,發送的數據纔會被父級v-model=“test”接受到
//input才能被v-model監聽
}
}
}
綁定Style中帶有圖片/圖片訪問不到
//使用require或者import導入,只有這樣,webpack打包纔會導入圖片,不然會出現圖片訪問不到
return {
backgroundImage:'url(' + require('../../assets/2.jpg') + ')',
backgroundPosition:this.bigDivx+'%' +' '+this.bigDivy+'%'
}
屬性有多個屬性值
// 空格隔開
backgroundPosition:this.bigDivx+'%' +' '+this.bigDivy+'%'
綁定style
<div class="zhezhao" :style="jisuanzuobiao" v-show="zhezhaoShow" ></div>
computed:{
jisuanzuobiao(){
let min = 0;
let max = 500;
let zhezhaoY = this.bianjie(this.zhezhaoY);
let zhezhaoX = this.bianjie(this.zhezhaoX);
return {top:zhezhaoY+'px', left:zhezhaoX+'px'}
}
}
訪問根實例vue/$root
通過this.$root
1.可以作爲全局的store使用,放入共享的數據
2.全局bus,自定義事件監聽和註冊,達到各個組件間的通信
自定義事件在程序中監聽/組件間的通行
$emit ("eventName",params) 發送一個事件
$on(eventName, eventHandler) 偵聽一個事件
$once(eventName, eventHandler) 一次性偵聽一個事件
$off(eventName, eventHandler) 停止偵聽一個事件
通過根vue完成通信操作(this.$root),
vue打包路徑問題/build不在服務器中打開空白/cordova打開爲空白
config/index.js文件修改一下位置
vue打包路徑問題/build不在服務器中打開空白---->cli-3.0
1.增加配置文件
2.去除路由器模式mode:history,默認爲mode:hash
vue中使用scss、sass
npm install node-sass --save-dev //只需要這句
npm install sass-loader --save-dev
<style lang="scss" scoped>
屏幕適配問題/設計稿寬計算
//在需要使用的文件import 'vw_base.js'
//375爲設計稿寬,一般寬爲750,可以改成實際設計稿寬
$vm_base: 375;
@function vw($px) {
@return ($px / 375) * 100vw;
}
阿里圖標庫
在main.js中引入iconfont.css樣式
import 'xxx/xxx/xxx/iconfont.css'
vscodeTab鍵不生效/emmet
文件-首選項-設置
v1.5版本後配置如下
"emmet.triggerExpansionOnTab": true,
"emmet.includeLanguages": {
"vue-html": "html",
"vue": "html"
}
設置代碼
"emmet.syntaxProfiles": {
"vue-html": "html",
"vue": "html"
}
操作dom/獲取到的數據不正確/$nextTick()
修改data中的數據,再到dom中獲取。這時候可能獲取到數據爲上一次的數據,dom還沒有完成刷新。使用this.$nextTick(funtion(){})
返回上一頁
$router.go(-1)
各種數據url鏈接/路由跳轉路徑path/全局訪問
1寫入一個文件中統一管理
2寫入在根vue中,統一'this.$url'調用,
3或者在main.js中 "var url = 導入的url" 變爲全局
//global/url.js
var url = {
index: '/data/index',
indexSwiper: '/data/indexSwiper',
indexClass1: '/data/indexClass1',
indexClass2: '/data/indexClass2',
listing: '/data/listing',
/**
* 導航路徑
*/
navigator: {
mainPage: {
selfpath: '/mainpage',
children:{
indexPath: 'index',
indexFullPath:'/mainpage/index',
orderPath: 'order',
orderFullPath:'/mainpage/order',
myPath: 'my',
myFullPath:'/mainpage/my',
}
},
classtifyAll:'/classtifyall',
searchPath:'/search'
}
}
export default url;
局域網訪問工程/非本機訪問
緩存/keep-alive/對router使用keep-alive
<keep-alive>
<router-view></router-view>
</keep-alive>
緩存滾動的位置/scrollTop
對組件A先使用keep-alive後,在組件A內緩存div滾動位置
<template>
<div class="index-root" id="mainContent">
........
</div>
</template>
data: function() {
return {
offsetTop:0,
};
},
activated() {
// keep-alive組件 頁面進入的時候設置滾動高度
document.getElementById("mainContent").scrollTop = this.offsetTop;
},
beforeRouteLeave(to, from, next) {
//組件離開的時候,獲取頁面滾動高度
this.offsetTop = document.getElementById('mainContent').scrollTop;
next()
},
深度/穿透/子組件樣式/deep/>>>
.header{
position: relative;
.mint-header{
height: 16vw;
/deep/ .mintui.mintui-back{ ////完成對子組件中類.mintui.mintui-back穿透
font-size: 6vw;
}
}
組件緩存/keep-alive/路由下的狀態保存
注意:對組件A使用時,包含A組件的父B不能保存狀態,B被路由替換,則A也是無效的。
app.vue中使用keep-alive包裹router-view
<keep-alive include="mainpage" > //<----------在組件中取名字name='mainpage',這可以激活狀態保
存。相對於的不包含屬性 exclude=''
<router-view></router-view>
</keep-alive>
router/index.js路由表中
/**
* mainpage頁,主頁
*/
{
path: nav.mainPage.selfpath,
name: 'mainpage',// 注意<---------App.vue中include='mainpage'不是對應這個name
component: MainPage,
children:[
{
path: nav.mainPage.children.indexPath,
name: 'index',
component: Index
},
{
path: nav.mainPage.children.myPath,
name: 'my',
component: My
},
{
path: nav.mainPage.children.orderPath,
name: 'order',
component: Order
}
]
},
在Mainpage.vue中定義name
export default {
name: 'mainpage',// <-------定義名字與<keep-alive>中include保持一致
data: function() {
return {
nameArray: ["首頁", "附近", "發現", "訂單", "我的"],
selected: "首頁",
// offsetTop:0,
};
},
......
組件全局的自動註冊/自動加載
下面代碼會自動全局註冊'/components'下的組件,
注意:下列代碼依賴'lodash'
/**
* 自動全局註冊componentS下的組件
*/
import {camelCase,upperFirst} from 'lodash'
import Vue from 'vue';
const requireComponent = require.context(
'@/components', //當前基礎組件相對與main.js的相對位置
false, //是否查詢子目錄
/\.(vue|js)$/
)
requireComponent.keys().forEach(fileName => {
const componentConfig = requireComponent(fileName) //獲取組建配置
const componentName =
fileName.replace(/^\.\/(.*)\.\w+$/, '$1') // 剝去文件名開頭的 `'./` 和結尾的擴展名
// const componentName = upperFirst( //獲取組件的 PascalCase 命名
// camelCase(
// fileName.replace(/^\.\/(.*)\.\w+$/, '$1') // 剝去文件名開頭的 `'./` 和結尾的擴展名
// )
// )
console.log(componentName)
// 全局註冊組件
Vue.component(
componentName,
componentConfig.default || componentConfig
)
})
返回上一頁/回退
<div @click="$router.go(-1)">
<slot></slot>
</div>
全局https網絡請求/this.$https調用
import axios from 'axios';
import url from '../global/url'
var https={
listing:{
request(){
return axios.get(url.listing).then((res)=>res.data)
}
},
registerInVue(Vue){
Vue.prototype.$https = https
}
}
export default https;
main.js
/**https訪問數據,爲index.js文件
* 可以在子vue中通過 this.$url調用
*/
import https from './https/index.js'
https.registerInVue(Vue);
子組件插槽問題?
父中傳入插槽所需內容,而內容中,有多個tag需要處理點擊事件,但是傳入的dom結果作用域是父的,然而需要傳入的dom響應子組件內的方法
注意:父組件模板的所有東西都會在父級作用域內編譯;子組件模板的所有東西都會在子級作用域內編譯。--官網
方式1:個人
mount()方法中,查找(querySelectAll(),)到需要點擊事件的node,並對node添加事件
方式2:官網
提示:將子組件作用域方法或值數組傳出給父
路由守衛
1.路由器守衛
2.路由全局守衛
3.組件內守衛
4.守衛共享
router/guard.js
import store from '../store/index'
import {Toast} from 'mint-ui'
var guard = {
/**
* user守衛
*/
userGuard:(to, from, next) => {
if(store.getters['user/loginState']){
next();
}else{
Toast({
message: '請先登錄',
position: 'bottom',
duration: 3000
});
// next({ path: '/login' })
next(false)
}
}
}
export default guard;
router/index.js
import guard from './guard.js'//------->導入守衛處理函數
export default new Router({
mode:'history',
routes: [
.......
/**
* setting 個人設置頁面
*/
{
path:nav.settingPath,
name:"setting",
component:Setting,
beforeEnter: guard.userGuard//------>引用共享函數guard.userGuard
//------>路由器守衛
},
/**
* my-collection 我的收藏頁面
*/
{
path:nav.myCollectionPath,
name:"my-collection",
component:MyCollection,
//路由守衛
beforeEnter: guard.userGuard//------>引用共享函數guard.userGuard
},
.......
]
})
跨域
1.找到 vue項目目錄 > config > index.js
dev: {
env: require('./dev.env'),
// Paths
assetsSubDirectory: 'static',
assetsPublicPath: '/',
proxyTable: {
'/api': { // 這裏的 /api 是固定的
target: 'http://xxxx.cn', //你的服務器地址
changeOrigin: true, //改變源
pathRewrite: {
'^/api': 'http://xxxx.cn' // 意爲 用 /api 這個字符代替 後面的地址 在訪問接口時直接寫 /api ...... 就好了,這個/api可以是任意的字符。
}
}
},
……
}
2.在開發過程中請求數據時直接用 /api (與上面pathRewrite定義的字符保持一致)開頭 +後面的接口。
axios.get('/api/xxx.json', function (res) {
console.log(res)
}
異步路由/路由懶加載
以函數返回值形式導入,函數未執行
const Foo = () => import('./Foo.vue')
將導入函數放進router對象,按需求,異步導入執行導入函數
const router = new VueRouter({
routes: [
{ path: '/foo', component: Foo }
]
})
把組件按組分塊(可不做)
const Foo = () => import(/* webpackChunkName: "group-foo" */ './Foo.vue')
const Bar = () => import(/* webpackChunkName: "group-foo" */ './Bar.vue')
const Baz = () => import(/* webpackChunkName: "group-foo" */ './Baz.vue')
注意
如果您使用的是 Babel,你將需要添加 syntax-dynamic-import
(當前新建工程已經存在改插件)插件,才能使 Babel 可以正確地解析語法。
父組件主動獲取子組件的數據和方法
1.調用子組件的時候 定義一個ref
<t-tab ref="xxx"></headerchild>//定義的名字xxx,父可以通過xxx調用t-tab
1
2.在父組件裏面通過
this.$refs.xxx.屬性----------->xxx同上
this.$refs.xxx.方法----------->xxx同上
子組件主動獲取父組件的數據和方法在子組件裏面通過
this.$parent.屬性
this.$parent.方法
子組件無法獲取到父傳入的props
1.父在mounted()中獲取的數據,修改需改綁定在props上的值,
2.錯誤的原因:子不可以在mounted中去獲取,不然報錯,因爲此時子已經mounted,父的mounted後修改數據在傳入晚了。
3.子應該在watch中完成獲取數據和操作。
this.$el.offsetHeight獲取爲0
原因1:當前vue被隱藏v-show=false;,獲取爲0。所以不能再初始化中運行獲取函數。
原因2:v-show=true;需要在$nextTick(function(){})中獲取,並且必須重新運行獲取函數。
動態加載圖片/static
將圖片放入static中就行,所有動態圖片都可以加載。如果放在assets中,就需要import或者require加載
爲路由器加入動畫/animate/transition
需要安裝animate.css動畫庫
基本操作
1.html結構
<template>
...
<transition
name="custom-classes-transition" ------------>表示自定義動畫
:enter-active-class="transitionInName"------------->表示進入時的動畫
:leave-active-class="transitionOutName"------------>表示出去時的動畫
>
<keep-alive include="mainpage">
<!-- <keep-alive > -->
<router-view></router-view>
</keep-alive>
</transition>
...
</template>
2.
data() {
return {
transitionInName:'animated slideInRight',
transitionOutName:'animated slideOutLeft'
}
},
。。。。。。。
watch: {
'$route' (to, from) {
if(this.$global.isRouterForward){------------->關鍵點
//前進動畫
this.transitionInName = 'animated slideInRight'
this.transitionOutName = 'animated animated-delay slideOutLeft'
}else{
//回退動畫
this.transitionInName = 'animated slideInLeft'
this.transitionOutName = 'animated animated-delay slideOutRight'
}
// 默認爲true
this.$global.isRouterForward = true;------------->關鍵點
}
}
global.js
isRouterForward : true,
back的監聽,需要修改標誌位isRouterForward =false;表示執行回退動畫
this.$global.isRouterForward = false;
this.$router.back()
頁面切換動畫----結合官網的寫法
<template>
<div id="app">
<transition
:enter-active-class="transitionInName"
:leave-active-class="transitionOutName">
<router-view />
</transition>
<SlideMenu></SlideMenu>
</div>
</template>
<script >
import SlideMenu from "@/views/slide-menu/Index.vue";
export default {
components: {
SlideMenu
},
data() {
return {
transitionInName:'animated slideInRight',
transitionOutName:'animated slideOutLeft'
}
},
watch: {
'$route'(to, from) {
const toDepth = to.path.split('/').length
const fromDepth = from.path.split('/').length
this.transitionName = toDepth < fromDepth ? 'slide-right' : 'slide-left'
if(toDepth === fromDepth){
this.transitionInName = ''
this.transitionOutName = ''
}else if (toDepth > fromDepth) {
//前進動畫
this.transitionInName = 'animated slideInRight'
this.transitionOutName = 'animated animated-delay slideOutLeft'
} else {
//回退動畫
this.transitionInName = 'animated slideInLeft'
this.transitionOutName = 'animated animated-delay slideOutRight'
}
}
}
}
</script>
<style lang="scss">
#app {
/*font-family: "Microsoft Yahei",'Avenir', Helvetica, Arial, sans-serif;*/
font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB",
"Microsoft YaHei", "微軟雅黑", Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
}
#nav {
/*padding: 30px;*/
a {
font-weight: bold;
color: #2c3e50;
&.router-link-exact-active {
color: #42b983;
}
}
}
.animated {
/*animation-duration: 60s !important;*/
animation-duration: 100s !important;
position: absolute;
}
</style>
注意:沒有進入動畫
因爲:進入的組件在出去組件下方,對於手機界面,必然看不見。
.animated{//在
animation-duration: 0.5s !impontant;---->加上權重
position:absolute----------->動畫不執行、不顯示,<transition>加上absolute
}
或者官方解決辦法
給<transition>加屬性 mode='in-out'。mode='out-in'執行效果難受,先完全出去,在執行進入動畫
或者(。。。。。)只要前進時:右側進入動畫,後退時:右側出去動畫
加上position:absolute後,動畫執行過程中抖動。
因爲在執行動畫過程中,父的寬出現的變化。解決:給每一個route(每一頁)的根加上定寬。
動畫執行過程中,header(position: fixed)部分不見了,動畫結束後顯示。
動畫過程中,header部分跑到了頁面最上,相當於position:fixed 沒有效果。當動畫結束,position:fixed 產生效果,header部分回來。
解決辦法:頁高(height:100vh),header部分定高後,main部分(可滾動的部分),讓他撐開(flex:1),讓main部分顯示自己滾動條,而不是使用body的滾動條。
keep-alive沒必要添加include
我以爲會出現Android和react那種情況,每次路由push一個頁A,就會是一個新的頁A,導致棧中出現好多個A頁。事實上,棧中只會有一個頁A被重複彈出到棧頂。
vue計算屬性get和set的作用和使用場景
<input v-model="sosoInput" type="text" :placeholder="tips">
。。。。。。。
computed: {
sosoInput: {
// getter
get: function () {
return this.$store.state.common.searchKeyword;------->getter從store獲取數據
},
// setter
set: function (newValue) {
this.sosoInputValue = newValue;------->set將數據放入data的sosoInputValue中
}
}
},
。。。。。。。
例子中,沒有將數據返回到store,因爲需要最後‘確認’點擊確定的按鈕,纔會通過store.commit()提交出去
遮罩處理
處理遮罩問題,當點擊遮罩div的子,也會觸發closePopup()函數,
辦法1.在vue中加上'.self'修飾符
<div class="zhezhao" @click.self="closePopup">
辦法2.事件目標對象===.zhezhao對象,即點擊觸發在div.zhezhao上,而非div.zhezhao的子類
if($event.target===this.$el.querySelector('.zhezhao')){
Vue學習之x-template(單文件.vue不需要)
<html>
<head>
<meta charset="utf-8">
<script src="https://unpkg.com/vue/dist/vue.min.js"></script>
</head>
<body>
<div id="app">
<my-component></my-component>
<script type="text/x-template" id="my-component">
<div>
<p>This is the content of component</p>
<p>Hello Vue!</p>
</div>
<div>Hello</div>
</script>
</div>
<script>
Vue.component('my-component',{
template:'#my-component'
});
var app=new Vue({
el:"#app"
});
</script>
</body>
</html>
router跳轉不能獲取到參數
const router = new VueRouter({
routes: [
{
path: '/user',
name: 'user',
component: User
}
]
})
正確的
1.name和param傳遞參數和
<!-- 命名的路由 -->
<router-link :to="{ name: 'user', params: { userId: 123 }}">User</router-link>
2.path和query傳遞參數
<!-- 帶查詢參數,下面的結果爲 /register?plan=private -->
<router-link :to="{ path: 'register', query: { plan: 'private' }}">Register</router-link>
錯誤的--混用
<router-link :to="{ path: 'user', params: { userId: 123 }}">User</router-link>
CSS Modules
注意:不需要按照官網配置module:true;配合scss不需要配置;
<style>
上添加 module
特性:
<style module>
.red {
color: red;
}
.bold {
font-weight: bold;
}
</style>
使用1:$style
的計算屬性
<template>
<p :class="$style.red">
This should be red
</p>
</template>
使用2::class
的對象/數組語法:
<template>
<div>
<p :class="{ [$style.red]: isRed }">
Am I red?
</p>
<p :class="[$style.red, $style.bold]">
Red and bold
</p>
</div>
</template>
使用3:通過 JavaScript 訪問
<script>
export default {
created () {
console.log(this.$style.red)
// -> "red_1VyoJ-uZ"
// 一個基於文件名和類名生成的標識符
}
}
</script>
全局自動導入scss的function、變量、mixin\scss自動導入
yarn add style-resources-loader -D
vue.config.js
const path = require('path')
module.exports = {
chainWebpack: config => {
const types = ['vue-modules', 'vue', 'normal-modules', 'normal']
types.forEach(type => addStyleResource(config.module.rule('scss').oneOf(type)))
},
}
function addStyleResource (rule) {
rule.use('style-resource')
.loader('style-resources-loader')
.options({
patterns: [
path.resolve(__dirname, './src/css/g2.scss'),//./src/css/g2.scss目標文件位置
],
})
}
子組件不刷新問題,created鉤子不執行
子組件不刷新問題,created鉤子不執行。
解決:給子組件綁定key值,當給子組件數據變化,相應的改變綁定key值。使得vue判斷這不是同一組件
router-view和key
做法與子組件刷新類似,給router-view綁定key值
1. 不設置 router-view 的 key 屬性
由於 Vue 會複用相同組件, 即 /page/1 => /page/2 或者 /page?id=1 => /page?id=2 這類鏈接跳轉時, 將不在執行created, mounted之類的鉤子, 這時候你需要在路由組件中, 添加beforeRouteUpdate鉤子來執行相關方法拉去數據
相關鉤子加載順序爲: beforeRouteUpdate
2. 設置 router-view 的 key 屬性值爲 $route.path
從/page/1 => /page/2, 由於這兩個路由的$route.path並不一樣, 所以組件被強制不復用, 相關鉤子加載順序爲:
beforeRouteUpdate => created => mounted
從/page?id=1 => /page?id=2, 由於這兩個路由的$route.path一樣, 所以和沒設置 key 屬性一樣, 會複用組件, 相關鉤子加載順序爲:
beforeRouteUpdate
3. 設置 router-view 的 key 屬性值爲 $route.fullPath
從/page/1 => /page/2, 由於這兩個路由的$route.fullPath並不一樣, 所以組件被強制不復用, 相關鉤子加載順序爲:
beforeRouteUpdate => created => mounted
從/page?id=1 => /page?id=2, 由於這兩個路由的$route.fullPath並不一樣, 所以組件被強制不復用, 相關鉤子加載順序爲:
beforeRouteUpdate => created => mounted
給函數添加抖動/封裝method
methods: {
forbiddenPass: debounce(function (index, row) {
//自己的代碼
}, 300)
}
注1:這裏只能使用function聲明函數,如果使用箭頭函數,會出錯
注2:也就說明不管是抖動,其他用lodash包裝過的函數都可以這麼寫
vue-cli打包出現代碼不分離,可以使用第二種形式\代碼分離的多種寫法
第一種寫法:官方推薦第一種
component: (resolve) => { require(['./views/Home.vue'], resolve) }
第二種寫法,兼容性好
// component: () => import('./views/Home.vue')
打包,基本配置
const CompressionPlugin = require('compression-webpack-plugin')
module.exports = {
publicPath: './', // 配置打包時的相對路徑
productionSourceMap: false, // 生產環境是否生成 SourceMap
devServer: {
port: 8080,
proxy: {
'/dev-api': {
// target: 'http://118.25.139.110:8088/', // target host
target: 'http://api.thdtek.com', // target host
// target: 'http://192.168.1.211:8080/', // target host
ws: true, // proxy websockets
changeOrigin: true, // needed for virtual hosted sites
pathRewrite: {
'^/dev-api': '' // rewrite path
}
}
}
},
configureWebpack: config => {
if (process.env.NODE_ENV === 'production') {
return {
plugins: [
new CompressionPlugin({
test: /\.js$|\.html$|\.css/, // 匹配文件名
threshold: 10240, // 對超過10k的數據壓縮
deleteOriginalAssets: false // 不刪除源文件
})
]
// cdn方式加載
// externals: {
// 'vue': 'Vue',
// 'vue-router': 'VueRouter',
// 'element-ui': 'ELEMENT',
// 'normalize': 'normalize',
// },
}
}
},
chainWebpack: config => {
config.output.filename('js/[name].[hash]' + '.' + Date.now() + '.js').end()
config.output.chunkFilename('js/[name].[chunkhash]' + '.' + Date.now() + '.js').end()
}
}
打包--瀏覽器緩存問題,每次發版本,出現不更新js文件情況
在打包時,給輸出的名字帶上時間戳(Date.now())
module.exports = {
chainWebpack: config => {
config.output.filename('js/[name].[hash]' + '.' + Date.now() + '.js').end()
config.output.chunkFilename('js/[name].[chunkhash]' + '.' + Date.now() + '.js').end()
}
打包--文件太大,使用gzip壓縮
const CompressionPlugin = require('compression-webpack-plugin')
module.exports = {
configureWebpack: config => {
if (process.env.NODE_ENV === 'production') {
return {
plugins: [
new CompressionPlugin({
test: /\.js$|\.html$|\.css/, // 匹配文件名
threshold: 10240, // 對超過10k的數據壓縮
deleteOriginalAssets: false // 不刪除源文件
})
]
// cdn方式加載
// externals: {
// 'vue': 'Vue',
// 'vue-router': 'VueRouter',
// 'element-ui': 'ELEMENT',
// 'normalize': 'normalize',
// },
}
}
},
}
路由A進入下一級B,緩存A;路由B進入下一級C,緩存B;B回退到A不緩存B
讓webstorm支持jsx語法
<script type="text/jsx">