前端【VUE】13
-
一、基本使用
1、介绍
Vant 是一个轻量、可靠的移动端组件库,于 2017 年开源。
目前 Vant 官方提供了 Vue 2 版本、Vue 3 版本和微信小程序版本,并由社区团队维护 React 版本和支付宝小程序版本。
2、安装
1 # Vue 3 项目,安装最新版 Vant:
2 npm i vant -S
3
4 # Vue 2 项目,安装 Vant 2:
5 npm i vant@latest-v2 -S
3、项目引入
创建vue项目,然后在main.js中引入vant组件
方式一. 自动按需引入组件 (推荐)
babel-plugin-import 是一款 babel 插件,它会在编译过程中将 import 的写法自动转换为按需引入的方式。
1 # 安装插件
2 npm i babel-plugin-import -D
配置
1 // 在.babelrc 中添加配置
2 // 注意:webpack 1 无需设置 libraryDirectory
3 {
4 "plugins": [
5 ["import", {
6 "libraryName": "vant",
7 "libraryDirectory": "es",
8 "style": true
9 }]
10 ]
11 }
12
13 // 对于使用 babel7 的用户,可以在 babel.config.js 中配置
14 module.exports = {
15 plugins: [
16 ['import', {
17 libraryName: 'vant',
18 libraryDirectory: 'es',
19 style: true
20 }, 'vant']
21 ]
22 };
方式二. 手动按需引入组件
1 import Button from 'vant/lib/button';
2 import 'vant/lib/button/style';
方式三. 导入所有组件
Vant 支持一次性导入所有组件,引入所有组件会增加代码包体积,因此不推荐这种做法
1 import Vue from 'vue';
2 import Vant from 'vant';
3 import 'vant/lib/index.css';
4
5 Vue.use(Vant);
4、组件注册
全局注册
全局注册后,你可以在 app 下的任意子组件中使用注册的 Vant 组件。
1 import Vue from 'vue';
2 import { Button } from 'vant';
3
4 // 方式一. 通过 Vue.use 注册
5 // 注册完成后,在模板中通过
或 标签来使用按钮组件 6 Vue.use(Button);
7
8 // 方式二. 通过 Vue.component 注册
9 // 注册完成后,在模板中通过
标签来使用按钮组件 10 Vue.component(Button.name, Button);
局部注册
局部注册后,你可以在当前组件中使用注册的 Vant 组件。
1 import { Button } from 'vant';
2
3 export default {
4 components: {
5 [Button.name]: Button,
6 },
7 };
4、使用
main.js内容
1 import Vue from 'vue'
2 import App from './App.vue'
3
4 // 导入组件
5 import Vant from 'vant'
6 // 导入组件样式库
7 import 'vant/lib/index.css'
8 // 全局注册vant
9 Vue.use(Vant)
10
11 Vue.config.productionTip = false
12
13 new Vue({
14 render: h => h(App)
15 }).$mount('#app')
在App.vue中使用相关组件
1
2
3
主要按钮 4
信息按钮 5
默认按钮 6
警告按钮 7
危险按钮 8
9
10
11 export default {
12 }
13
14
15
启动服务,查看样式加载情况,npm run serve
二、适配的配置
vw适配
官方说明:https://vant-contrib.gitee.io/vant/v2/#/zh-CN/advanced-usage
1、下载依赖
1 npm i postcss-px-to-viewport@1.1.1 -D
2 安装报错,则:
3 npm i postcss-px-to-viewport@1.1.1 -D --legacy-peer-deps
2、新增配置
项目根目录, 新建postcss的配置文件postcss.config.js
1 // postcss.config.js
2 module.exports = {
3 plugins: {
4 'postcss-px-to-viewport': {
5 // 设计稿如果是2倍图,宽是750,则 750/2 = 375,下面就写375
6 // 设计稿如果是3倍图,宽是 1080,则 1080/3 = 360,下面就写360
7 viewportWidth: 375
8 }
9 }
10 }
3、修改了配置需要重启服务
REM适配
1、下载依赖包
1 npm i postcss-pxtorem -D
2 npm i lib-flexible
3 报错解决,安装时,加 --legacy-peer-deps 选项
2、main.js中导入所需的js
1 import 'lib-flexible'
3、新增配置
项目根目录,创建 postcss.config.js,把下面的代码加入进去即可
1 module.exports = {
2 plugins: {
3 'postcss-pxtorem': {
4 rootValue: 37.5,
5 propList: ['*'],
6 },
7 },
8 };
4、修改配置重启服务
定制主题
第一步:
方式1:按需引入样式(推荐)
在 babel.config.js 中配置按需引入样式源文件,注意 babel6 不支持按需引入样式,请手动引入样式。
1 module.exports = {
2 plugins: [
3 [
4 'import',
5 {
6 libraryName: 'vant',
7 libraryDirectory: 'es',
8 // 指定样式路径
9 style: (name) => `${name}/style/less`,
10 },
11 'vant',
12 ],
13 ],
14 };
方式2:手动引入
1 // 引入全部样式
2 import 'vant/lib/index.less';
3
4 // 引入单个组件样式
5 import 'vant/lib/button/style/less';
第二步:
使用 Less 提供的 modifyVars 即可对变量进行修改,下面是参考的 webpack 配置。
1 // webpack.config.js
2 module.exports = {
3 rules: [
4 {
5 test: /\.less$/,
6 use: [
7 // ...其他 loader 配置
8 {
9 loader: 'less-loader',
10 options: {
11 // 若 less-loader 版本小于 6.0,请移除 lessOptions 这一级,直接配置选项。
12 lessOptions: {
13 modifyVars: {
14 // 直接覆盖变量
15 'text-color': '#111',
16 'border-color': '#eee',
17 // 或者可以通过 less 文件覆盖(文件路径为绝对路径)
18 // hack: `true; @import "your-less-file-path.less";`,
19 },
20 },
21 },
22 },
23 ],
24 },
25 ],
26 };
如果 vue-cli 搭建的项目,可以在 vue.config.js 中进行配置。
1 // vue.config.js
2 module.exports = {
3 css: {
4 loaderOptions: {
5 less: {
6 // 若 less-loader 版本小于 6.0,请移除 lessOptions 这一级,直接配置选项。
7 lessOptions: {
8 modifyVars: {
9 // 直接覆盖变量
10 'text-color': '#111',
11 'border-color': '#eee',
12 // 或者 如果要修改的样式比较多,可以通过 less 文件覆盖(文件路径为绝对路径)
13 // hack: `true; @import "your-less-file-path.less";`, aaaa: 'red' // 项目中组件的颜色color : @aaa, 此时就会根据这里的变量进行颜色的覆盖
14 },
15 },
16 },
17 },
18 },
19 };
三、面经项目
基础依赖下载
1 // 1、脚手架
2 npm i @vue/cli -g
3
4
5 // 2、下载vant
6 npm i vant@latest-v2 -S
1、目录结构
2、src/api下的调用接口的js
article.js
1 import request from '@/utils/request'
2
3 // 获取文章列表接口封装完成
4 export const getArticles = (obj) => {
5 return request.get('/interview/query', {
6 // get请求参数
7 params: {
8 current: obj.current || 1, // 当前页 → 做分页
9 pageSize: obj.pageSize || 10, // 每页条数 → 做分页
10 sorter: obj.sorter // 排序字段 → 推荐(weight_desc) & 最新(不传参数)
11 }
12 // // headers请求头,配置token
13 // headers: {
14 // Authorization: `Bearer ${token}`
15 // }
16 })
17 }
18
19 // 获取文章详情数据
20 export const getArticleDetail = (id) => {
21 return request.get('interview/show', {
22 params: {
23 id
24 }
25 })
26 }
27
28 // 点赞接口
29 export const updateLike = (id) => {
30 return request.post('interview/opt', {
31 id,
32 optType: 1 // 喜欢
33 })
34 }
35
36 // 收藏接口
37 export const updateCollect = (id) => {
38 return request.post('interview/opt', {
39 id,
40 optType: 2 // 收藏
41 })
42 }
43
44 // 获取我的收藏
45 export const getArticlesCollect = (obj) => {
46 return request.get('/interview/opt/list', {
47 params: {
48 page: obj.page, // 当前页
49 pageSize: 5, // 可选
50 optType: 2 // 表示收藏
51 }
52 })
53 }
54
55 // 获取我的喜欢
56 export const getArticlesLike = (obj) => {
57 return request.get('/interview/opt/list', {
58 params: {
59 page: obj.page, // 当前页
60 pageSize: 5, // 可选
61 optType: 1 // 表示喜欢
62 }
63 })
64 }
user.js
1 // 本文件用于存放所有和用户相关的请求函数
2 import request from '@/utils/request'
3
4 // 1. 注册用户
5 export const register = (data) => {
6 // 进行注册请求
7 // 注意:这里必须 return,将请求的promise对象返回,将来才能await拿结果
8 return request.post('/user/register', data)
9 }
10
11 // 2. 账户登录
12 export const login = (data) => {
13 // 进行登录请求
14 return request.post('/user/login', data)
15 }
16
17 // 3. 获取用户信息
18 export const getUserInfo = () => {
19 return request('/user/currentUser')
20 }
3、公共组件components目录下
1
2
3
4
5
6
7
8
{{ item.stem }}
9
{{ item.creator }} | {{ item.createdAt }}
10
11
12
13
14
15 {{ clearHTMLTag(item.content) }}
16
17
点赞 {{ item.likeCount }} | 浏览 {{ item.views }}18
19
20
21
22
23 export default {
24 name: 'ArticleItem',
25 props: {
26 item: {
27 type: Object,
28 default: () => {}
29 }
30 },
31 methods: {
32 // 清除 html 标记符号,将标签符号替换为 ""
33 // /<[^>]+>/g 匹配所有标签符号
34 clearHTMLTag (str) {
35 return str.replace(/<[^>]+>/g, '')
36 }
37 }
38 }
39
40
41
42 .article-item {
43 .head {
44 display: flex;
45 img {
46 width: 40px;
47 height: 40px;
48 border-radius: 50%;
49 overflow: hidden;
50 }
51 .con {
52 flex: 1;
53 overflow: hidden;
54 padding-left: 10px;
55 p {
56 margin: 0;
57 line-height: 1.5;
58 &.title {
59 width: 280px;
60 }
61 &.other {
62 font-size: 10px;
63 color: #999;
64 }
65 }
66 }
67 }
68 .body {
69 font-size: 14px;
70 color: #666;
71 line-height: 1.6;
72 margin-top: 10px;
73 }
74 .foot {
75 font-size: 12px;
76 color: #999;
77 margin-top: 10px;
78 }
79 }
80
4、路由router目录下
index.js
1 import Vue from 'vue'
2 import VueRouter from 'vue-router'
3 import { getToken } from '@/utils/storage'
4
5 // 一级路由页面的导入
6 const Login = () => import('@/views/Login')
7 const Register = () => import('@/views/Register')
8 const Layout = () => import('@/views/Layout')
9 const Detail = () => import('@/views/Detail')
10
11 // 二级路由页面的导入
12 const Article = () => import('@/views/Article')
13 const Collect = () => import('@/views/Collect')
14 const Like = () => import('@/views/Like')
15 const User = () => import('@/views/User')
16
17 Vue.use(VueRouter)
18
19 const router = new VueRouter({
20 routes: [
21 { path: '/login', component: Login },
22 { path: '/register', component: Register },
23 { path: '/article/:id', component: Detail },
24 {
25 path: '/',
26 component: Layout,
27 redirect: '/article',
28 children: [
29 { path: '/article', component: Article },
30 { path: '/like', component: Like },
31 { path: '/collect', component: Collect },
32 { path: '/user', component: User }
33 ]
34 }
35 ]
36 })
37
38 // 白名单(就是一个数组,数组收录所有无需登录即可访问的页面)
39 const whiteList = ['/login', '/register']
40
41 // 路由前置守卫
42 // 1. to 往哪去
43 // 2. from 从哪来
44 // 3. next 是否放行 next() 放行 next(路径) 拦截到某个页面
45 router.beforeEach((to, from, next) => {
46 const token = getToken()
47 if (token) {
48 // 如果有token,直接放行
49 next()
50 } else {
51 // 没有token的人,看看要去哪
52 // 判断访问的路径 to.path, 是否在白名单中
53 if (whiteList.includes(to.path)) {
54 next()
55 } else {
56 next('/login')
57 }
58 }
59 })
60
61 export default router
5、工具utils目录下
request.js
1 import axios from 'axios'
2 import { Toast } from 'vant'
3 import { getToken, delToken } from './storage'
4 import router from '@/router/index'
5
6 // 创建自定义的实例
7 const instance = axios.create({
8 baseURL: 'http://interview-api-t.itheima.net/h5/',
9 timeout: 5000
10 })
11
12 // 自定义配置 (请求和响应拦截器)
13 // 添加请求拦截器
14 instance.interceptors.request.use(function (config) {
15 // 在发送请求之前做些什么
16 const token = getToken()
17 // 统一携带token
18 if (token) {
19 config.headers.Authorization = `Bearer ${token}`
20 }
21 return config
22 }, function (error) {
23 // 对请求错误做些什么
24 return Promise.reject(error)
25 })
26
27 // 添加响应拦截器
28 instance.interceptors.response.use(function (response) {
29 // 对响应数据做点什么
30 return response.data
31 }, function (error) {
32 // console.log(error)
33 // 有错误响应,后台正常返回了错误信息
34 if (error.response) {
35 if (error.response.status === 401) {
36 // 清除掉无效的token
37 delToken()
38 // 拦截到登录
39 router.push('/login')
40 } else {
41 // 有错误响应,提示错误消息
42 // this.$toast(error.response.data.message)
43 Toast(error.response.data.message)
44 }
45 }
46 // 对响应错误做点什么
47 return Promise.reject(error)
48 })
49
50 // 导出实例
51 export default instance
storage.js 公共存储
1 const KEY = 'vant-mobile-exp-token'
2
3 // 1. 获取token (需要返回token)
4 export const getToken = () => {
5 return localStorage.getItem(KEY)
6 }
7 // 2. 设置token
8 export const setToken = (newToken) => {
9 localStorage.setItem(KEY, newToken)
10 }
11 // 3. 删除token
12 export const delToken = () => {
13 localStorage.removeItem(KEY)
14 }
vant按需导入的vant-ui.js
1 // 按需导入:将需要的vant组件,逐一导入
2 import {
3 Grid,
4 GridItem,
5 CellGroup, Icon, List, Cell, Toast, NavBar, Form, Field, Button, Rate, Tabbar, TabbarItem
6 } from 'vant'
7 import Vue from 'vue'
8 Vue.use(Grid)
9 Vue.use(GridItem)
10 Vue.use(CellGroup)
11 Vue.use(Icon)
12 Vue.use(List)
13 Vue.use(Cell)
14 Vue.use(Toast)
15 Vue.use(NavBar)
16 Vue.use(Form)
17 Vue.use(Field)
18 Vue.use(Tabbar)
19 Vue.use(TabbarItem)
20 Vue.use(Button)
21 Vue.use(Rate)
6、views目录下的视图
Article.vue
1
2
3
4
10 >
11
12
13 :class="{ active: sorter === null }"
14 @click="changeSorter(null)"
15 href="javascript:;"
16 >最新
17 >
18

19
20
21
22 v-model="loading"
23 :finished="finished"
24 finished-text="没有更多数据了"
25 @load="onLoad"
26 >
27
28
29
30
31
32
33
34 import { getArticles } from '@/api/article'
35 export default {
36 name: 'article-page',
37 data () {
38 return {
39 current: 1, // 当前页
40 sorter: 'weight_desc', // 推荐
41 list: [], // 文章列表
42 loading: false, // 是否在加载中
43 finished: false // 是否加载完了全部的数据
44 }
45 },
46 async created () {
47 // 获取推荐的,第1页的10条数据
48 // const res = await getArticles({
49 // current: this.current,
50 // sorter: this.sorter
51 // })
52 // this.list = res.data.rows
53 // console.log(res.data.rows)
54 },
55 methods: {
56 async onLoad () {
57 const res = await getArticles({
58 current: this.current,
59 sorter: this.sorter
60 })
61 console.log(res)
62 // 需要在 this.list 基础上,累加 res.data.rows
63 this.list.push(...res.data.rows)
64 // 如果数据已经请求完毕,需要将loading改成false,才能加载下一页的数据
65 // 一旦 loading 改为 false,load事件可以再次触发
66 this.loading = false
67 this.current++ // 当前页+1
68
69 // 对于没有更多数据的数据
70 if (this.current > res.data.pageTotal) {
71 this.finished = true
72 }
73 },
74 changeSorter (value) {
75 // 修改排序规则 (推荐/最新)
76 this.sorter = value
77
78 // 重置数据
79 this.current = 1 // 排序条件变化,重新从第一页开始加载
80 this.list = [] // 数据重置为空
81 this.finished = false // finished 重置,重新有数据可以加载了
82 // this.loading = false
83
84 // 标记需要开始加载了,由于我们是手动调用加载更多 onLoad 方法
85 // 所以loading 需要自己改成 true,避免重复触发
86 this.loading = true
87 // 根据最新的条件重新渲染
88 this.onLoad()
89 }
90 }
91 }
92
93
94
95 .article-page {
96 margin-bottom: 50px;
97 margin-top: 44px;
98 .my-nav {
99 height: 44px;
100 position: fixed;
101 left: 0;
102 top: 0;
103 width: 100%;
104 z-index: 999;
105 background: #fff;
106 display: flex;
107 align-items: center;
108 > a {
109 color: #999;
110 font-size: 14px;
111 line-height: 44px;
112 margin-left: 20px;
113 position: relative;
114 transition: all 0.3s;
115 &::after {
116 content: '';
117 position: absolute;
118 left: 50%;
119 transform: translateX(-50%);
120 bottom: 0;
121 width: 0;
122 height: 2px;
123 background: #222;
124 transition: all 0.3s;
125 }
126 &.active {
127 color: #222;
128 &::after {
129 width: 14px;
130 }
131 }
132 }
133 .logo {
134 flex: 1;
135 display: flex;
136 justify-content: flex-end;
137 > img {
138 width: 64px;
139 height: 28px;
140 display: block;
141 margin-right: 10px;
142 }
143 }
144 }
145 }
146
Collect.vue
1
2
3
4
5 v-model="loading"
6 :finished="finished"
7 finished-text="没有更多了"
8 @load="onLoad"
9 >
10
11
12
13
14
15
16 import { getArticlesCollect } from '@/api/article'
17 export default {
18 name: 'collect-page',
19 data () {
20 return {
21 list: [],
22 loading: false,
23 finished: false,
24 page: 1
25 }
26 },
27 methods: {
28 async onLoad () {
29 // 异步更新数据
30 const { data } = await getArticlesCollect({ page: this.page })
31 this.list.push(...data.rows)
32 this.loading = false
33 this.page++
34
35 if (this.page > data.pageTotal) {
36 this.finished = true
37 }
38 }
39 }
40 }
41
42
43
44 .collect-page {
45 margin-bottom: 50px;
46 margin-top: 44px;
47 }
48
Detail.vue
1
2
3
4 left-text="返回"
5 @click-left="$router.back()"
6 fixed
7 title="面经详细"
8 />
9
10
{{ article.stem }}
11
12 {{ article.createdAt }} | {{ article.views }} 浏览量 |
13 {{ article.likeCount }} 点赞数
14
15
16
17 {{ article.creator }}
18
19
20
21
22
23
24
25
26
27
28
29 import { getArticleDetail, updateCollect, updateLike } from '@/api/article'
30
31 export default {
32 name: 'detail-page',
33 data () {
34 return {
35 article: {}
36 }
37 },
38 async created () {
39 this.article = {}
40 const { data } = await getArticleDetail(this.$route.params.id)
41 this.article = data
42 },
43 methods: {
44 async toggleLike () {
45 await updateLike(this.article.id)
46 this.article.likeFlag = !this.article.likeFlag
47 if (this.article.likeFlag) {
48 this.article.likeCount++
49 this.$toast.success('点赞成功')
50 } else {
51 this.article.likeCount--
52 this.$toast.success('取消点赞')
53 }
54 },
55 async toggleCollect () {
56 await updateCollect(this.article.id)
57 this.article.collectFlag = !this.article.collectFlag
58 if (this.article.collectFlag) {
59 this.$toast.success('收藏成功')
60 } else {
61 this.$toast.success('取消收藏')
62 }
63 }
64 }
65 }
66
67
68
69 .detail-page {
70 margin-top: 44px;
71 overflow: hidden;
72 padding: 0 15px;
73 .header {
74 h1 {
75 font-size: 24px;
76 }
77 p {
78 color: #999;
79 font-size: 12px;
80 display: flex;
81 align-items: center;
82 }
83 img {
84 width: 40px;
85 height: 40px;
86 border-radius: 50%;
87 overflow: hidden;
88 }
89 }
90 .opt {
91 position: fixed;
92 bottom: 100px;
93 right: 0;
94 > .van-icon {
95 margin-right: 20px;
96 background: #fff;
97 width: 40px;
98 height: 40px;
99 line-height: 40px;
100 text-align: center;
101 border-radius: 50%;
102 box-shadow: 2px 2px 10px #ccc;
103 font-size: 18px;
104 &.active {
105 background: #FEC635;
106 color: #fff;
107 }
108 }
109 }
110 }
111
Layout.vue
1
2
3
4
5
6
7
8
面经 9
收藏 10
喜欢 11
我的 12
13
14
15
16
17 export default {
18 name: 'LayoutPage'
19 }
20
21
22
Like.vue
1
2
3
4
5 v-model="loading"
6 :finished="finished"
7 finished-text="没有更多了"
8 @load="onLoad"
9 >
10
11
12
13
14
15
16 import { getArticlesLike } from '@/api/article'
17 export default {
18 name: 'like-page',
19 data () {
20 return {
21 list: [],
22 loading: false,
23 finished: false,
24 page: 1
25 }
26 },
27 methods: {
28 async onLoad () {
29 // 异步更新数据
30 const { data } = await getArticlesLike({ page: this.page })
31 this.list.push(...data.rows)
32 this.loading = false
33 this.page++
34
35 if (this.page > data.pageTotal) {
36 this.finished = true
37 }
38 }
39 }
40 }
41
42
43
44 .like-page {
45 margin-bottom: 50px;
46 margin-top: 44px;
47 }
48
Login.vue
1
2
3
4
5
6
7
8
9 v-model="username"
10 name="username"
11 label="用户名"
12 placeholder="请输入用户名"
13 :rules="[
14 { required: true, message: '请填写用户名' },
15 { pattern: /^\w{5,}$/, message: '用户名至少包含5个字符'}
16 ]"
17 />
18
19 v-model="password"
20 type="password"
21 name="password"
22 label="密码"
23 placeholder="请输入密码"
24 :rules="[
25 { required: true, message: '请填写密码' },
26 { pattern: /^\w{6,}$/, message: '密码至少包含6个字符' }
27 ]"
28 />
29
30
提交 31
32
33
34
注册账号 35
36
37
38
39 import { login } from '@/api/user'
40 import { setToken } from '@/utils/storage'
41 export default {
42 name: 'LoginPage',
43 data () {
44 return {
45 username: '',
46 password: ''
47 }
48 },
49 methods: {
50 // 监听表单的提交,形参中:可以获取到输入框的值
51 async onSubmit (values) {
52 const { data } = await login(values)
53 // 1. 成功的提示
54 this.$toast('登录成功')
55 // 2. 将token存入本地
56 setToken(data.token)
57 // 3. 跳转首页
58 this.$router.push('/')
59 }
60 }
61 }
62
63
64
65 .link {
66 color: #069;
67 font-size: 12px;
68 padding-right: 20px;
69 float: right;
70 }
71
Register.vue
1
2
3
4
5
6
7
8
9 v-model="username"
10 name="username"
11 label="用户名"
12 placeholder="请输入用户名"
13 :rules="[
14 { required: true, message: '请填写用户名' },
15 { pattern: /^\w{5,}$/, message: '用户名至少包含5个字符'}
16 ]"
17 />
18
19 v-model="password"
20 type="password"
21 name="password"
22 label="密码"
23 placeholder="请输入密码"
24 :rules="[
25 { required: true, message: '请填写密码' },
26 { pattern: /^\w{6,}$/, message: '密码至少包含6个字符' }
27 ]"
28 />
29
30
提交 31
32
33
34
有账号,去登陆 35
36
37
38
39 // import { Toast } from 'vant'
40 import { register } from '@/api/user'
41 export default {
42 name: 'LoginPage',
43 data () {
44 return {
45 username: '',
46 password: ''
47 }
48 },
49 methods: {
50 // 监听表单的提交,形参中:可以获取到输入框的值
51 async onSubmit (values) {
52 // const res = await register(values)
53 // console.log(res)
54
55 // Toast('注册成功')
56 // Toast.loading({
57 // message: '拼命加载中...',
58 // forbidClick: true
59 // })
60 // Toast.success('成功文案')
61 // Toast.fail('失败文案')
62
63 // 其实Toast方法已经被挂到了原型上,通过 this.$toast 直接调用
64 this.$toast.loading({
65 message: '请求中...',
66 forbidClick: true
67 })
68
69 await register(values)
70 this.$toast.success('注册成功')
71 this.$router.push('/login')
72 }
73 }
74 }
75
76
77
78 .link {
79 color: #069;
80 font-size: 12px;
81 padding-right: 20px;
82 float: right;
83 }
84
User.vue
1
2
3
4
5
{{ username }}
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 import { getUserInfo } from '@/api/user'
24 import { delToken } from '@/utils/storage'
25 export default {
26 name: 'user-page',
27 data () {
28 return {
29 username: '',
30 avatar: ''
31 }
32 },
33 async created () {
34 const { data } = await getUserInfo()
35 this.username = data.username
36 this.avatar = data.avatar
37 },
38 methods: {
39 logout () {
40 delToken()
41 this.$router.push('/login')
42 }
43 }
44 }
45
46
47
48 .user-page {
49 padding: 0 10px;
50 background: #f5f5f5;
51 height: 100vh;
52 .mt20 {
53 margin-top: 20px;
54 }
55 .user {
56 display: flex;
57 padding: 20px 0;
58 align-items: center;
59 img {
60 width: 80px;
61 height: 80px;
62 border-radius: 50%;
63 overflow: hidden;
64 }
65 h3 {
66 margin: 0;
67 padding-left: 20px;
68 font-size: 18px;
69 }
70 }
71 }
72
7、根组件App.vue
1
2
3
4
5
6
7
8
9 export default {
10 data () {
11 return {
12
13 }
14 }
15 }
16
17
18
19
20
8、main.js
1 import Vue from 'vue'
2 import App from './App.vue'
3 import router from './router'
4
5 // 全部导入:将所有的vant组件,一次性导入到项目中
6 // import Vant from 'vant'
7 // import 'vant/lib/index.css'
8 // Vue.use(Vant)
9
10 // 导入按需导入的配置文件
11 import '@/utils/vant-ui'
12
13 // 组件的全局注册
14 import ArticleItem from '@/components/ArticleItem'
15 Vue.component('ArticleItem', ArticleItem)
16
17 Vue.config.productionTip = false
18
19 new Vue({
20 router,
21 render: h => h(App)
22 }).$mount('#app')
9、package.json
1 {
2 "name": "hm-exp-mobile",
3 "version": "0.1.0",
4 "private": true,
5 "scripts": {
6 "serve": "vue-cli-service serve",
7 "build": "vue-cli-service build",
8 "lint": "vue-cli-service lint"
9 },
10 "dependencies": {
11 "axios": "^1.3.5",
12 "core-js": "^3.8.3",
13 "vant": "^2.12.54",
14 "vue": "^2.6.14",
15 "vue-router": "^3.5.1"
16 },
17 "devDependencies": {
18 "@babel/core": "^7.12.16",
19 "@babel/eslint-parser": "^7.12.16",
20 "@vue/cli-plugin-babel": "~5.0.0",
21 "@vue/cli-plugin-eslint": "~5.0.0",
22 "@vue/cli-plugin-router": "~5.0.0",
23 "@vue/cli-service": "~5.0.0",
24 "@vue/eslint-config-standard": "^6.1.0",
25 "babel-plugin-import": "^1.13.6",
26 "eslint": "^7.32.0",
27 "eslint-plugin-import": "^2.25.3",
28 "eslint-plugin-node": "^11.1.0",
29 "eslint-plugin-promise": "^5.1.0",
30 "eslint-plugin-vue": "^8.0.3",
31 "less": "^4.0.0",
32 "less-loader": "^8.0.0",
33 "postcss-px-to-viewport": "1.1.1",
34 "vue-template-compiler": "^2.6.14"
35 }
36 }
10、postcss.config.js
1 // postcss.config.js
2 module.exports = {
3 plugins: {
4 'postcss-px-to-viewport': {
5 // 标准屏的宽度,设计图 750-2倍图,标准屏 375
6 viewportWidth: 375
7 }
8 }
9 }
11、vue.config.js
1 const { defineConfig } = require('@vue/cli-service')
2 module.exports = defineConfig({
3 transpileDependencies: true,
4 // 将资源访问路径从 / 配置成 ./ 相对路径
5 publicPath: './',
6 css: {
7 loaderOptions: {
8 less: {
9 // 若 less-loader 版本小于 6.0,请移除 lessOptions 这一级,直接配置选项。
10 lessOptions: {
11 modifyVars: {
12 // 直接覆盖变量
13 blue: '#FA6D1D'
14 }
15 }
16 }
17 }
18 }
19 })
打包发布
1 // yarn管理
2 yarn build
3
4 // npm 管理
5 npm run build
配置publicPath
1 module.exports = {
2 // 设置获取.js,.css文件时,是以相对地址为基准的。
3 // https://cli.vuejs.org/zh/config/#publicpath
4 publicPath: './'
5 }
路由懒加载
路由懒加载 & 异步组件, 不会一上来就将所有的组件都加载,而是访问到对应的路由了,才加载解析这个路由对应的所有组件
官网链接:https://router.vuejs.org/zh/guide/advanced/lazy-loading.html#%E4%BD%BF%E7%94%A8-webpack
当打包构建应用时,JavaScript 包会变得非常大,影响页面加载。如果我们能把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应组件,这样就更加高效了。
1 const Detail = () => import('@/views/detail')
2 const Register = () => import('@/views/register')
3 const Login = () => import('@/views/login')
4 const Article = () => import('@/views/article')
5 const Collect = () => import('@/views/collect')
6 const Like = () => import('@/views/like')
7 const User = () => import('@/views/user')