VUE项目
实现登录页面
-
复制一遍
Register.vue, 改名:Login.vue -
修改页面文本内容。
-
去掉重复密码。
Navbar组件
navbar组件用于实现顶部选项卡(导航)。其基本语法结构如下:
<mt-navbar v-model="navactive">
<mt-tab-item id="1">人文</mt-tab-item>
<mt-tab-item id="2">教育</mt-tab-item>
<mt-tab-item id="3">科技</mt-tab-item>
<mt-tab-item id="4">数码</mt-tab-item>
</mt-navbar>
data() {
return {
navactive : '1'
}
},
案例: 访问/navbar 测试navbar。
mt-tab-item提供了一个名为icon的插槽,可以通过该插槽设置导航项的图标。
在脚手架项目中可以存放的位置有两个: public/ src/assets/
public下的图片如何访问? src/assets下的图片如何访问?
public下的图片用绝对路径访问(/开头的路径) 例如: 有如下图片: /public/images/001.png vue中的访问方式: <img src="/images/001.png"> src/assets下的图片用相对路径访问: 例如: 有如下资源: /src/assets/001.png vue中的访问方式: <img src="../assets/001.png"> <img src="@/assets/001.png">
在脚手架的设计理念下,public属于静态资源目录,不在编译的范围内。而src文件夹下的资源都应属于源代码的一部分,无论是vue文件,还是图片文件,都应该被统一编译处理。所以vue在加载assets下的图片时,将会把这些 图片重新命名,统一放入一个/img/目录下,同时篡改src路径,使得可以正常访问该图片。
什么时候把图片放入public,什么时候将图片放入src/assets ?
public属于普通静态资源目录,页面中需要显示的大图,更新比较频繁的图片适合放在public下。
assets是需要经过编译的,所以经常存放项目中必要的小图标等资源,作为源代码的一部分,编译到项目中。而这个编译图片的过程也会对图片访问做一些性能上的优化:
若图片足够小,将会在编译时将该图片转成base64图片字符串,直接写入src:
<img src="base64:imagexx/xxxxxxjasdfhaskfhsdlkqwrqwr">
将会在页面初次加载时,就会把图片完整显示在页面中,不会导致由于网络环境导致小图标显示不全的问题。
TabContainer组件
TabContainer组件用于实现面板,页面中可以保存多个面板,并且实现面板内容的切换。其基本语法结构如下:
<mt-tab-container v-model="navactive">
<mt-tab-container-item id="1">面板1</mt-tab-container-item>
<mt-tab-container-item id="2">面板2</mt-tab-container-item>
<mt-tab-container-item id="3">面板3</mt-tab-container-item>
<mt-tab-container-item id="4">面板4</mt-tab-container-item>
</mt-tab-container>
实现首页中的页面布局
完善首页中的标题栏、顶部导航、面板等组件的配置。
Tabbar组件
Tabbar组件用于实现底部选项卡。 其语法结构:
<mt-tabbar v-model="selected">
<mt-tab-item id="shouye">首页</mt-tab-item>
<mt-tab-item id="gouwuche">购物车</mt-tab-item>
<mt-tab-item id="wode">我的</mt-tab-item>
</mt-tabbar>
data:{
selected: 'shouye'
}
案例: 新建testing/Tabbar.vue,测试底部选项卡的用法。访问/tabbar时看到组件效果。
动态图片的路径设置问题
若有一个img访问如下资源:
<img slot="icon" src="@/assets/main_1.png" alt="">
vuecli将会把该图片进行编译,放入/img/下,并且篡改src路径:
<img slot="icon" src="/img/main_1.8e7c1489.png" alt="">
若希望src内的路径为动态路径,在不同的情况下引用不同的图片时,需要为src添加冒号,变为动态路径:
<img slot="icon" :src="'@/assets/main_1.png'" alt="">
注意:一旦为src添加冒号设置为动态路径,vuecli将不会对该路径进行处理。最终编译为:
<img src="@/assets/main_1.png">
所以,若想要动态访问src下的图片,需要在路径外添加require方法,require方法将会对该资源进行编译,并且返回编译后的路径:
<img :src="require('@/assets/main_1.png')" >
实现首页中底部选项卡
-
搭建
Home.vue底部选项卡的结构。默认选中“首页”。 -
新建
Me.vue。底部选项卡默认选中“我的”。 -
实现两个页面中切换底部选项卡时的业务流程。
Swipe组件
swipe组件用于实现轮播图,其语法结构为:
<mt-swipe>
<mt-swipe-item>
<img src="......">
</mt-swipe-item>
......
</mt-swipe>
案例: 访问/swipe 看到testing/Swipe.vue。
项目架构
基于B/S架构的项目。 使用http协议作为网络通信协议。服务端数据存储在mysql数据库中。前后端分离架构。
客户端:MintUI
服务端:nodejs + mysql
通信协议: http
搭建学子问答项目服务端
下载server.zip, 解压缩。 下载xzqa.sql, 导入mysql数据库。 打开xampp, 启动mysql服务, 点击shell按钮,进入命令行, 执行命令: mysql -uroot -p < [把sql文件拖拽进来生成路径] # 检查数据库 mysql -uroot -p # 进入mysql MariaDB [(none)]> show databases; MariaDB [(none)]> use xzqa; MariaDB [(none)]> show tables; MariaDB [(none)]> select * from xxxxx; 启动server,提供web数据服务: cd server node app.js 在浏览器中测试接口。 http://localhost:3000/category http://localhost:3000/articles?cid=1&page=1
项目实现
加载首页中的类别列表信息
实现步骤
安装配置axios。
下载安装aixos:
cd scaffolding
npm install --save axios
配置main.js:
import axios from 'axios'
axios.defaults.baseURL = 'http://localhost:3000/'
Vue.prototype.axios = axios
在mounted中发送axios请求,获取类别列表,渲染页面。
/** 初始化导航列表 */
initNav(){
this.axios.get('/category').then(res=>{
console.log(res)
// 将 res.data.results 存入 this.cats 中
this.cats = res.data.results
})
}
<mt-navbar style="margin-top: 40px"
v-model="navactive" fixed>
<mt-tab-item
v-for="(item, i) in cats" :key="i" :id="item.id+''">
{{item.category_name}}
</mt-tab-item>
</mt-navbar>
加载首页UI类别下的文章列表
实现步骤
-
编写文章列表项的页面组件结构与布局。
-
在
mounted中发送http请求,访问ui类别下的首页数据。
Swipe组件
swipe组件用于实现轮播图,其语法结构为:
<mt-swipe>
<mt-swipe-item>
<img src="......">
</mt-swipe-item>
......
</mt-swipe>
案例: 访问/swipe 看到testing/Swipe.vue。
切换顶部选项卡更新文章列表
由于每个选项卡的布局都一样,切换选项卡时仅仅需要更新当前列表数据即可,没必要那么多的面板。干掉仨,留下第一个即可。
实现步骤:
-
编写
watch监听navactive的变化。一旦navactive有变化就意味着用户更改了顶部导航选中项。 -
一旦顶部导航更新了选中项,获取当前选中项的类别
id(就是newvalue), 将id作为参数传给/articles地址查询该类别下的首页数据。 -
获取响应数据后,替换当前列表即可。
列表触底分页加载
mintui提供了Infinite Scroll指令(无限滚动指令)用于监听触底事件。其基本用法:
<div
v-infinite-scroll="loadmore" 一旦触底将会执行loadmore方法
infinite-scroll-distance="触发事件时距离底部的阈值">
<p>....</p>
<p>....</p>
<p>....</p>
<p>....</p>
<p>....</p>
<p>....</p>
.....
</div>
VUEUI Unit05
列表触底分页加载
mintui提供了Infinite Scroll指令(无限滚动指令)用于监听触底事件。其基本用法:
<div
v-infinite-scroll="loadmore" 一旦触底将会执行loadmore方法
infinite-scroll-distance="触发事件时距离底部的阈值"
infinite-scroll-disabled="infDisabled"
:infinite-scroll-immediate-check="true">
<p>....</p>
<p>....</p>
<p>....</p>
<p>....</p>
<p>....</p>
<p>....</p>
.....
</div>
触底加载下一页的业务逻辑
声明一个变量维护“当前页码”:page. 一旦触底loadmore, page++ 在loadmore方法中发送http请求,请求当前类别下的下一页数据。 优化: 应该loadmore方法正在执行过程中,禁用无限滚动。 当loadmore方法请求发送完毕,响应接收完毕, 数据处理完毕后,再把无限滚动给打开。 一旦获取响应数据,将得到的新的一页文章列表 追加到当前文章列表的末尾。 /articles?cid=1&page=1 /articles?cid=1&page=2 /articles?cid=1&page=3 /articles?cid=1&page=4 .....
封装一个方法:loadArticles()用于异步加载文章列表
至此发现触底加载下一页时发送的请求,与切换选项卡、mounted后发送的请求非常类似,都是要加载文章列表。向同一个地址发请求,传递不同的参数,接收列表数据后更新列表。
不一样的地方是:传递参数不同,还有就是接收到响应后的处理方式不同 : mounted与切换选项卡后替换列表,而loadmore是要追加列表。可以设计一个方法, 封装这个功能,提高代码的重用性以及可维护性。
loadArticles方法是一个异步方法,该方法需要异步发送请求,并且把响应后的结果返回给方法的调用者。这个业务场景有两种解决方案:
callback
promise
loadArticles(cid, page, callback){
this.axios.get('/articles?${cid}&${page}').then(res=>{
let arts = res.data.results
callback(arts)
})
}
mounted(){
this.loadArticles(1, 1, (arts)=>{
// arts 就是服务端获取文章数组
this.articles = arts
} )
}
watchnav(){
this.loadArticles(xxx, 1, (arts)=>{})
}
loadmore(){
this.loadArticles(xx, xxxx, (arts)=>{})
}
文章详情页的实现
需求是点击首页中某一个文章列表项后,跳转到文章详情页 ,并且传递当前选中文章的id。 在详情页中接收该id参数,发送http请求, 获取详情数据, 更新界面。
实现步骤
-
准备好
Article.vue。解压缩,复制粘贴, 配置路由。
/article<--->Article.vue -
通过
routerlink,跳转页面,并且传参。vue中通过router-link路由跳转并且携带参数的方式有两种:第一种:使用?的方式向第二个页面传参。
<router-link to="/article?name=x&id=xx">
</router-link>详情页中如下方式接收
id与name:mounted(){
this.$route.query.id
this.$route.query.name
}第二种:使用
路径参数向第二个页面传参。<router-link to="/article/237">
</router-link>如下方式接收:
{
path: '/article/:id',
component: Article
}mounted(){
this.$route.params.id
} -
接收参数,发送请求,获取响应,更新界面。
moment.js
moment.js是一个日期时间的js类库。 可以运行在浏览器及Node环境下。
moment.js的常用操作
解析(想方设法 获取一个Moment对象)
let day = moment() // 描述 当前时刻 的 Moment对象
let day = moment('2020-10-12') // 通过字符串创建Moment对象
day = moment('10-13/2020', 'MM-DD/YYYY')
day = moment('2020-10-01 11:11:11', 'YYYY-MM-DD HH:mm:ss')
day = moment.unix(秒级时间戳)
显示(将Moment对象输出成字符串)
let day = moment()
day.format() --> '2021-10-10T08:10:20xxxx'
day.format('YYYY年MM月DD日') --> '2020年12月12日'
安装配置moment.js
# 在项目目录下执行命令: npm install moment --save // 在main.js中,引入moment import moment from 'moment' Vue.prototype.moment = moment
实现详情页回到首页后,保留首页的状态
回到详情页:
<mt-button @click="$router.go(-1)"
icon="back" slot="left"></mt-button>
回到详情页后发现首页又做了一次初始化,显示UI类别下的首页数据。所以需要让Home.vue保活(keepAlive)。
在App.vue中添加keepAplive让vuerouter保活其中涉及到的组件:
<keep-alive>
<router-view />
</keep-alive>
这么做虽然Home.vue保活了,但是重新点击文章列表项看另外一篇文章时,发现Article.vue也保活了,导致无法触发mounted,当然也就没有办法更新文章详情了。所以,又有一个新需求,Article.vue不需要保活。如下配置:
只有route中meta的keepAlive属性为true时,才保活
<keep-alive>
<router-view v-if="$route.meta.keepAlive"/>
</keep-alive>
<router-view v-if="!$route.meta.keepAlive"/>
还需要在路由中(index.js)添加meta,指定什么路由需要keepAlive:
{
path: '/article',
component: Article,
meta: {
keepAlive: false // Article 不希望被缓存
}
},
{
path: '/',
name: 'Home',
component: Home,
meta: {
keepAlive: true // 希望被缓存
}
},
保活业务完成后,又发现一个bug,当在Article.vue中滚动时,竟然会触发Home.vue的无限滚动。所以希望离开Home.vue时禁用无限滚动,回到Home.vue时再开启无限滚动的功能即可。这里需要使用keepAlive相关的两个生命周期方法:activated (当重新激活Home.vue时执行) deactivated(当离开Home.vue时执行)
activated(){ this.infDisabled=false },
deactivated(){ this.infDisabled=true },
实现注册业务
注册表单验证成功后,发送请求执行注册。
let params = `username=${this.name}&password=${this.pwd}`
this.axios.post('/register', params).then(res=>{
console.log(res)
})
注册完毕后若返回成功,需要去数据库中验证是否注册了正确的数据。
mysql -uroot -p 进入mysql
select * from xzqa_author;
比较密码:是不是md5加密后的密码: select md5('123123');
登录业务
完成登录页面的表单验证。 发送post请求,执行登录操作。 针对不同的响应结果给出不同的界面呈现。 登录成功后,将用户存入vuex,其它页面中若需要当前登录用户则直接去vuex中找即可。
Vuex
Vuex是一个专门为vue.js应用程序开发的状态管理模式。它采用集中式的存储来管理应用中涉及到的状态信息(state)。
Vuex的基本使用
state 存储数据
state: {
name:'zs'
},
如下访问:
this.$store.state.name
mutations 定义方法,修改state
mutations: {
updateName(state, newName){
state.name = newName
}
},
如何访问:
//vuex,帮我调用updateName这个mutatitions,修改name,给你传个参数:zhangsan
this.$store.commit('updateName', 'zhangsan')
actions 定义方法,异步操作后调用mutations修改state
actions: {
login(store, userinfo){
axios.post('/login', ...).then(res=>{
store.commit('updateName', name)
})
}
},
如何访问actions:
this.$store.dispatch('login', xxxxxxxx)
vuex的确可以在不刷新的情况下解决用户状态管理的问题。但是一旦刷新页面,将会重新加载vue实例, 自然会销毁vuex中保存的所有的状态。如果需要持久化存储一些状态, 可以使用HTML5新特性中webstorage来解决。
所以通常情况下,vuex都需要与webstorage结合在一起实现一些需求。
WebStorage
webStorage提供了localStorage和SessionStorage用于在客户端持久化存储键值对数据。
localStorage提供了一个独立的数据存储区域, 这部分数据将会永久存储在客户端本地。
sessionStorage提供了一个独立的数据存储区域,这部分数据只在当前会话中生效,关闭浏览器后将会自动销毁。
webstorage的基本使用
sessionstorage与localstorage都可以通过window对象直接获取:
let ss = window.sessionStorage
let ls = window.localStorage
然后直接调用ls、ss的方法,即可对这两个存储空间进行操作:
如何存?
ss.setItem('name', 'zs')
ls.setItem('islogin', true)
如何取?
ss.getItem('name')
ls.getItem('islogin')
如何删?
ss.removeItem('name')
ls.removeItem('islogin')
如何清空?
ss.clear()
ls.clear()
实现用户状态的持久化保存
-
在登录成功后,将
islogin与name存入sessionStorage。 -
当刷新页面时,
vuex将会初始化,我们需要重置vuex中islogin与name两个变量的初始化值,取sessionStorage中读取即可。
vuecli项目上线流程
链接:https://pan.baidu.com/s/1BqWxsbs00qLkfCJ6E4peEQ
提取码:7lln

浙公网安备 33010602011771号