只執(zhí)行一次的命令
1.安裝vscode擴展插件
vetur和Vue 3 Snippets
2.打開vscode終端gitbash--安裝vue-cli腳手架
npm install -g @vue/cli@4
3.查看vue腳手架是否安裝成功
vue --version
4.創(chuàng)建一個新項目
vue create fresh (fresh是項目的名稱--自定義)
5.執(zhí)行命令
npm run serve
6.devtool在谷歌瀏覽器上下載插件
下載極簡插件地址: https://chrome.zzzmh.cn/#/index 搜索vue找到需要下載的插件,安裝在谷歌瀏覽器上
7.安裝less模塊,允許使用less編寫樣式
npm install less less-loader@6.0.0 --save-dev
(在style里面編寫less樣式時,style標簽后面需要添加樣式格式,也就是lang="less")
配置簡單的路由
1.安裝路由插件
npm i vue-router@3.0
2.在src下創(chuàng)建自定義文件夾views,在此創(chuàng)建各個模塊的組件

模塊的內(nèi)容可被修改

3.在src文件夾中創(chuàng)建新自定義文件夾router,
在router文件夾中創(chuàng)建一個自定義文件inex.js
3.1:在index.js文件中配置并導出路由模塊
//導入模塊
import Vue from "vue";
import Router from "vue-router";
//使用模塊
Vue.use(Router);
// 路由數(shù)組
const routes = [
{
//根目錄地址加/goods就是商品頁面的地址
path: "/goods",
//這個頁面的在文件中的地址
component: () => import("../views/goods/goodsList.vue")
},
{
path: "/type",
component: () => import("../views/goods/type.vue")
},
{
path: "/cart",
component: () => import("../views/cart/cart.vue")
},
{
path: "/my",
component: () => import("../views/my/my.vue")
}
];
const router = new Router({
routes
});
//導出路由模塊
export default router;
3.2:掛載路由
在自動生成的main.js文件夾輸入內(nèi)容
// main.js 代碼
import Vue from 'vue'
import App from './App.vue'
//導入這個模塊路由
import router from './router/index'
?
Vue.config.productionTip = false
?
new Vue({
// 把router掛載到vue實例
router,
render: h => h(App),
}).$mount('#app')
3.3:配置子路由出口(把子路由對應(yīng)的組件渲染在什么地方)
App.vue是根路由文件,在不跳轉(zhuǎn)到其它頁面的情況下,顯示的是最首頁的頁面內(nèi)容
配置子路由
在App.vue文件中輸入路由出口
<template>
<div id="app">
<!-- 路由出口 -->
<router-view></router-view>
</div>
</template>
這個時候,當輸入商品頁面的接口時,就可以輸入/goods,就能跳轉(zhuǎn)到商品頁面
4.底部欄,路由跳轉(zhuǎn)
在App.vue文件中輸入靜態(tài)和樣式
<template>
<div id="app">
<!-- 子路由出口(子路由對應(yīng)的組件展示在這里) -->
<router-view></router-view>
<footer>
//利用to='地址'實現(xiàn)點擊該鏈接,跳轉(zhuǎn)到這個路由對應(yīng)的頁面
//router-link就相當與a標簽
<router-link to="/goods">首頁</router-link>
<router-link to="/type">分類</router-link>
<router-link to="/cart">購物車</router-link>
<router-link to="/my">我的</router-link>
</footer>
</div>
</template>
<style lang="less">
body {
background: #f4f4f4;
}
footer {
position: fixed;
bottom: 0;
height: 50px;
background: #fff;
display: flex;
justify-content: space-around;
align-items: center;
width: 100%;
}
</style>
5.active-class
active-class是vue-router模塊的router-link組件中的屬性,用來做選中樣式的切換;
//目前在App.vue頁面中
<footer>
<router-link active-class="active" to="/goods">首頁</router-link>
<router-link active-class="active" to="/type">分類</router-link>
<router-link active-class="active" to="/cart">購物車</router-link>
<router-link active-class="active" to="/my">我的</router-link>
</footer>
6.history模式
vue-router 默認 hash 模式 —— 使用 URL 的 hash 來模擬一個完整的 URL,于是當 URL 改變時,頁面不會重新加載。
如果不想要很丑的 hash,我們可以用路由的 history 模式,這種模式充分利用 history.pushState API 來完成 URL 跳轉(zhuǎn)而無須重新加載頁面
使用history需要后端支持, vue-cli創(chuàng)建的devServer可以支持
注意:history模式需要服務(wù)器的支持
//index.js
const router = new VueRouter({
mode: 'history', // 默認hash
routes: [...]
})
7. redirect重定向
當訪問 '/', 我們使用redirect使它默認跳到 '/product'這個路由地址頁面
//index.js
{
path: '/',
redirect: '/product'
},
8.404配置
假如用戶訪問了一個沒有的路由, 我們讓它跳轉(zhuǎn)到404頁面
在index.js文件中添加,并且在components文件夾中添加一個文件,NotFound(自定義的,但是要和下面的名字一樣)
//index.js
{
path: '*',
component:()=>import('@/components/NotFound')
}
9.配置子路由
9.1.首先,創(chuàng)建存放子路由的文件夾
在需要有子頁面的模塊中新建一個自定義文件夾,用來存放子頁面的路由.,如在goods模塊里面新建一個children文件夾,將goods有關(guān)的文件存放在這里.

9.2.子路由編寫內(nèi)容
分別子路由的頁面中編寫內(nèi)容,方便查看,(如:商品詳情)

9.3配置子路由
在上面新建配置路由的文件index.js中來配置子路由

配置子路由的內(nèi)容,子路由有一個chilrden數(shù)組,存放子頁面的路由接口,其中path:'list'里面不需要有'/',因為是子路由,會自動的拼接成'/goods/list'路由,下面的component中的是子頁面的存放地址

在到goods模塊里面的goods.vue頁面里面去配置出口

//goods.vue
<template>
<div>
<!-- 子路由出口 -->
<router-view></router-view>
<!-- 子路由跳轉(zhuǎn) -->
<router-link to="/goods/list"></router-link>
<router-link to="/goods/detail"></router-link>
</div>
</template>
<script>
export default {
}
</script>
<style>
</style>
其中可以看到goods模塊文件夾中有一個分類子頁面,沒有配置出口,是因為它出現(xiàn)在了底部欄中,(不過也可以和goodslist它們配置在同一個地方),但是這里為了方便,我們配置在了外面

將goods模塊的分類type.vue的子頁面配置在了外面,就是在index.js文件中.沒有子頁面的模塊,就只要配置自己的路由接口就可以了

10.編寫靜態(tài)
10.1:標簽元素和less樣式的位置
使用less格式編寫樣式的時候,必須添加lang='less'
編寫靜態(tài)標簽元素代碼時,必須把內(nèi)容都用一個div包起來
10.2:支持less格式操作
安裝插件,編寫代碼
npm install less less-loader@6.0.0 --save-dev

11.配置公共樣式和字體圖標
1:安裝插件
npm i vant@latest-v2 -S
2:生成公共樣式和字體圖標的樣式
字體圖標盡量下載到本地使用
樣式一般都放到src/assets文件夾下,新建一個自定義style文件夾,將公共樣式和字體圖標放入進去,并且注意將字體圖標單獨放一個文件件.

3.導入所有的樣式
在style文件夾中新建一個自定義文件index.js,將需要的樣式導入到這個文件中,然后外面使用時,就只要導入這個index.js文件就可以了
//index.js
//如果有多個也可以導入進來
import '../style/font/iconfont.css';
import '../style/common.css';
4.外部導入所有的樣式(包括安裝的插件樣式)
在main.js根文件中來導入樣式(注意:地址要對),這樣就可以被其它的頁面所使用了
圖1:導入公共樣式和字體圖標的樣式

圖2:導入插件的樣式
//main.js
import Vue from 'vue';
import App from './App.vue';
import router from './router/index';
import Vant from 'vant';
//公共樣式和字體圖標
import './assets/style/index.js';
import 'vant/lib/index.css';
//使用Vant插件樣式
Vue.use(Vant);
Vue.config.productionTip = false
new Vue({
router,
render: h => h(App),
}).$mount('#app')
5.使用公共樣式

12.父子組件通信
知識點
父傳子:父組件通過(綁定)屬性的方式傳數(shù)據(jù)給子組件,子組件使用props接收數(shù)據(jù)
子傳父:父組件在子組件上綁定一個自定義事件,子組件通過$emit觸發(fā)該自定義事件,同時可以傳入數(shù)據(jù)
1:組件的概念
可以將頁面的某一部分做成一個組件,比如頁面的導航欄,底部的tab欄等,組件可以重復(fù)使用
組件分為:局部組件和全局組件
2:全局組件
全局組件寫在main.js頁面中,哪里使用就直接使用不需要導入
通過Vue.component()來定義全局組件
定義全局組件必須放在new vue()
//main.js
//全局組件
//Hello就是這個全局組件名,(自定義的)
Vue.component('Hello',{
data(){
return{
title:'華夏中國'
}
},
template:`<h2>{{title}}</h2>`
})
哪一個頁面需要使用這個全局組件,就直接使用組件就可以了,不需要導入
//方式1
<Hello></Hello>
//方式2
<Hello/>

使用全局組件,需要在文件中新建一個文件vue.config.js(固定的)進行配置才能生效
注意:當配置了這個就需要重新啟動服務(wù)才可以,否則會一直報錯
const path = require('path');
module.exports = {
lintOnSave: false,
configureWebpack: (config) => {
config.resolve = {
extensions: [".js", ".vue", ".json", ".css"],
alias: {
vue$: "vue/dist/vue.esm.js",
"@": path.resolve("src")
}
};
}
};
3:局部組件
局部變量需要導入才能使用,盡量將公共局部組件寫在自動生成的components文件夾中
如:在components文件夾中新建一個局部組件Footer.vue(這是底部導航欄,一般都是大部分頁面需要的).然后在這個Footer.vue文件中編寫內(nèi)容即可

<template>
<div>
<footer class="fixed w100p bg-fff flex jc-sa aic f14">
<router-link active-class="active" to="/goods">
<p class="flex fdc jc-c aic">
<i class="iconfont icon-home"></i>
<span>首頁</span>
</p>
</router-link>
<router-link active-class="active" to="/type">
<p class="flex fdc jc-c aic">
<i class="iconfont icon-fenlei"></i>
<span>分類</span>
</p>
</router-link>
<router-link active-class="active" to="/cart">
<p class="flex fdc jc-c aic">
<i class="iconfont icon-gouwuchexian"></i>
<span>購物車</span>
</p>
</router-link>
<router-link active-class="active" to="/my">
<p class="flex fdc jc-c aic">
<i class="iconfont icon-wode"></i>
<span>我的</span>
</p>
</router-link>
<router-link active-class="active" to="/demo" href="./個人中心.html">
<p class="flex fdc jc-c aic">
<i class="iconfont icon-wode"></i>
<span>demo</span>
</p>
</router-link>
</footer>
</div>
</template>
<script>
export default {
}
</script>
<style lang="less">
footer{
bottom: 0;
height: 50px;
}
footer p .iconfont{
font-size: 20px;
}
a{
text-decoration: none;
color: #999;
}
.active{
color: red;
}
</style>
使用局部組件Footer
在使用局部組件的時候,需要先導入局部組件再使用components去注冊組件才能使用,哪個頁面使用該局部組件,就導入再注冊就可使用
注意:注冊多個組件,使用逗號分隔開
//假如是購物車頁面需要的底部導航欄組件
//cart.vue
<template>
<div>
<!-- 使用組件 -->
<Footer />
</div>
</template>
<script>
//導入底部導航欄組件
import Footer from '@/components/Footer.vue';
export default {
components:{
//注冊組件
Footer,
}
}
</script>
4:父傳子
父就相當與最外層來展示的頁面,子就是父頁面內(nèi)部的各個小模塊,這些小模塊通過props來接收父頁面?zhèn)鬟^來的數(shù)據(jù)
父組件通過屬性傳數(shù)據(jù)給子組件
子組件通過props接收父組件傳過來的數(shù)據(jù)
注意:1.通過普通屬性傳遞的數(shù)據(jù)類型是字符串
2.通過:(冒號)綁定屬性傳遞的數(shù)據(jù)類型是所傳數(shù)據(jù)的類型
例子1:父傳子
父頁面
<template>
<div>
<h1>demo父組件</h1>
<hr>
//使用局部組件時傳過去的數(shù)據(jù)
//:money="money"利用了冒號綁定事件來獲取data傳來的數(shù)據(jù)
<Son msg="hello world" :money="money" :count="true"/>
</div>
</template>
?
<script>
//導入局部組件
import Son from './Son.vue';
export default {
//注冊局部組件
components: {
Son
},
?
data() {
return {
money: 2000
}
}
};
</script>
子頁面
<template>
<div>
<h4>這是子組件</h4>
<!-- 使用接收的數(shù)據(jù) -->
<p>{{msg}}</p>
<p>{{money}}</p>
<p>{{typeof count}}</p>
</div>
</template>
?
<script>
export default {
// 接收msg,money,count屬性
props: ['msg','money','count']
}
</script>
例子2:父傳子的實踐
把生鮮首頁拆分為多個子組件
注意:一個組件一個頁面
<template>
<div>
//輪播圖的組件
<Swiper/>
//秒殺的組件
<Miaosha/>
//商品分類的組件
<TypeList/>
//底部導航欄的組件
<Footer/>
</div>
</template>
?
<script>
//導入各個組件
import Footer from '@/components/Footer.vue';
import Swiper from "./goodList/Swiper.vue";
import Miaosha from "./goodList/Miaosha.vue";
import TypeList from "./goodList/TypeList.vue";
export default {
//注冊各個組件
components: {
Footer,
Swiper,
Miaosha,
TypeList
}
};
</script>
5:子傳父
父組件在子組件上綁定了一個自定義事件(事件名稱我們自己定義,就是vue本身沒有的事件)
- 自定義事件再綁定一個函數(shù)
- 這個函數(shù)也可以是自定義的
- 子組件使用$emit觸發(fā)(調(diào)用)該事件,并把數(shù)據(jù)以參數(shù)的形式傳給父組件
例子1:一個簡單的例子
父組件頁面
<template>
<div>
<h3>父組件</h3>
<hr />
<Son @aaa="say"/>
</div>
</template>
?
<script>
import Son from "./Son";
export default {
components: {
Son,
},
data() {
return {
};
},
methods: {
say(data) {
alert(data)
}
}
};
</script>
子組件頁面
<template>
<div>
<h4>子組件</h4>
<button @click="handleClick">點擊</button>
</div>
</template>
?
<script>
export default {
methods: {
handleClick() {
this.$emit("aaa", "我是子組件");
}
}
};
</script>
例子2:自定義導航欄組件
//Header.vue
<template>
<div class="nav flex jc-sb aic pl-15 pr-15">
<!-- 觸發(fā)左邊的事件 -->
<p @click="$emit('click-left')">
<van-icon v-if="typeof leftArrow === 'string'" name="arrow-left" />
<span>{{ leftText }}</span>
</p>
<span>{{ title }}</span>
<!-- 觸發(fā)右邊的事件 -->
<span @click="handleClick">{{ rightText }}</span>
</div>
</template>
?
<script>
export default {
props: ["left-text", "title", "right-text", "left-arrow"],
?
methods: {
handleClick() {
// 做點事情
console.log(11111111111111);
// 觸發(fā)事件的同時給父組件傳了個對象
this.$emit("click-right", {username: '張三', age:18});
}
}
};
</script>
?
<style lang="less" scoped>
.nav {
height: 50px;
border: 1px solid;
}
</style>
//demo.vue
//哪個頁面需要就導入
<template>
<div>
<NavBar @click-left="onClickLeft" @click-right="onClickRight" left-text="返回" title="購物車" right-text="編輯" left-arrow/>
</div>
</template>
?
<script>
import NavBar from "@/components/NavBar.vue";
export default {
components: {
NavBar
},
?
data() {
return {
money: 2000
};
},
?
methods: {
onClickLeft() {
alert("點擊左邊");
},
onClickRight(data) {
alert(data.username);
}
}
};
</script>
13:配置axios攔截器
-
對ajax請求進行攔截
- 在請求頭添加token
-
對ajax響應(yīng)數(shù)據(jù)進行攔截
- 統(tǒng)一處理請求失敗的情況,這樣就不需要在每個組件里處理失敗的情況
- 有些接口需要登錄才能訪問,在沒登錄的情況下跳轉(zhuǎn)到登錄頁面
-
需要配置axios攔截器
在src文件夾下新建一個自定義文件夾api,在api文件夾下新建一個自定義文件ruquest.js
//ruquest.js
import axios from "axios";
import Vue from "vue";
import { Toast } from "vant";
Vue.use(Toast);
?
const service = axios.create({
baseURL: "http://huruqing.cn:3003",
timeout: 50000, // 請求超時時間(因為需要調(diào)試后臺,所以設(shè)置得比較大)
});
?
// request 對請求進行攔截
service.interceptors.request.use(
(config) => {
// 開啟loading
Toast.loading({
message: "加載中...",
forbidClick: true,
loadingType: "spinner",
});
// 請求頭添加token
config.headers["token"] =
"gg12j3h4ghj2g134kj1g234gh12jh34k12h34g12kjh34kh1g";
return config;
},
(error) => {
Promise.reject(error);
}
);
?
// response 響應(yīng)攔截器
service.interceptors.response.use(
(response) => {
Toast.clear();
const res = response.data;
if (res.code == 666) {
return res;
} else {
// 成功連接到后臺, 但是沒有返回正確的數(shù)據(jù)
Toast.fail(res.msg);
}
},
(error) => {
Toast.clear();
// 跟后臺連接失敗
Toast.fail("網(wǎng)絡(luò)異常,請稍后再試");
}
);
?
export default service;