php 企业网站框架,怎么做一个设计师网站,苏州哪个公司做网站好,wordpress 格式day1
接口文档地址#xff1a;https://www.apifox.cn/apidoc/project-934563/api-20384515
一、项目功能演示
1.目标
启动准备好的代码#xff0c;演示移动端面经内容#xff0c;明确功能模块 2.项目收获 二、项目创建目录初始化
vue-cli 建项目
1.安装脚手架 (已安装…day1
接口文档地址https://www.apifox.cn/apidoc/project-934563/api-20384515
一、项目功能演示
1.目标
启动准备好的代码演示移动端面经内容明确功能模块 2.项目收获 二、项目创建目录初始化
vue-cli 建项目
1.安装脚手架 (已安装)
npm i vue/cli -g2.创建项目
vue create hm-vant-h5选项
Vue CLI v5.0.8
? Please pick a preset:Default ([Vue 3] babel, eslint)Default ([Vue 2] babel, eslint)Manually select features 选自定义手动选择功能 选择vue的版本 3.x2.x是否使用history模式 选择css预处理 选择eslint的风格 eslint 代码规范的检验工具检验代码是否符合规范比如const age 18; 报错多加了分号后面有工具一保存全部格式化成最规范的样子 选择校验的时机 直接回车 选择配置文件的生成方式 直接回车 是否保存预设下次直接使用 不保存输入 N 等待安装项目初始化完成 启动项目
npm run serve三、ESlint代码规范及手动修复
代码规范一套写代码的约定规则。例如赋值符号的左右是否需要空格一句结束是否是要加;… 没有规矩不成方圆 ESLint:是一个代码检查工具用来检查你的代码是否符合指定的规则(你和你的团队可以自行约定一套规则)。在创建项目时我们使用的是 JavaScript Standard Style 代码风格的规则。
1.JavaScript Standard Style 规范说明
建议把https://standardjs.com/rules-zhcn.html 看一遍然后在写的时候, 遇到错误就查询解决。
下面是这份规则中的一小部分
字符串使用单引号 – 需要转义的地方除外无分号 – 这没什么不好。不骗你关键字后加空格 if (condition) { ... }函数名后加空格 function name (arg) { ... }坚持使用全等 摒弃 一但在需要检查 null || undefined 时可以使用 obj null…
2.代码规范错误
如果你的代码不符合standard的要求eslint会跳出来刀子嘴豆腐心地提示你。
下面我们在main.js中随意做一些改动添加一些空行空格。
import Vue from vue
import App from ./App.vueimport ./styles/index.less
import router from ./router
Vue.config.productionTip falsenew Vue ( {render: h h(App),router
}).$mount(#app)
按下保存代码之后
你将会看在控制台中输出如下错误 eslint 是来帮助你的。心态要好有错就改。 3.手动修正
根据错误提示来一项一项手动修正。
如果你不认识命令行中的语法报错是什么意思你可以根据错误代码func-call-spacing, space-in-parens,…去 ESLint 规则列表中查找其具体含义。
打开 ESLint 规则表使用页面搜索Ctrl F这个代码查找对该规则的一个释义。 四、通过eslint插件来实现自动修正 eslint会自动高亮错误显示通过配置eslint会自动帮助我们修复错误 如何安装 如何配置
// 当保存的时候eslint自动帮我们修复错误
editor.codeActionsOnSave: {source.fixAll: true
},
// 保存代码不自动格式化
editor.formatOnSave: false注意eslint的配置文件必须在根目录下这个插件才能才能生效。打开项目必须以根目录打开一次打开一个项目注意使用了eslint校验之后把vscode带的那些格式化工具全禁用了 Beatify
settings.json 参考
{window.zoomLevel: 2,workbench.iconTheme: vscode-icons,editor.tabSize: 2,emmet.triggerExpansionOnTab: true,// 当保存的时候eslint自动帮我们修复错误editor.codeActionsOnSave: {source.fixAll: true},// 保存代码不自动格式化editor.formatOnSave: false
}五、调整初始化目录结构 强烈建议大家严格按照老师的步骤进行调整为了符合企业规范 为了更好的实现后面的操作我们把整体的目录结构做一些调整。
目标:
删除初始化的一些默认文件修改没删除的文件新增我们需要的目录结构
1.删除文件
src/assets/logo.pngsrc/components/HelloWorld.vuesrc/views/AboutView.vuesrc/views/HomeView.vue
2.修改文件
main.js 不需要修改
router/index.js
删除默认的路由配置
import Vue from vue
import VueRouter from vue-routerVue.use(VueRouter)const routes [
]const router new VueRouter({routes
})export default router
App.vue
templatediv idapprouter-view//div
/template3.新增目录
src/api 目录 存储接口模块 (发送ajax请求接口的模块) src/utils 目录 存储一些工具模块 (自己封装的方法)
目录效果如下: 六、vant组件库及Vue周边的其他组件库 组件库第三方封装好了很多很多的组件整合到一起就是一个组件库。 https://vant-contrib.gitee.io/vant/v2/#/zh-CN/ 比如日历组件、键盘组件、打分组件、登录组件等
组件库并不是唯一的常用的组件库还有以下几种
pc: element-ui element-plus iview ant-design
移动vant-ui Mint UI (饿了么) Cube UI (滴滴)
七、全部导入和按需导入的区别
目标明确 全部导入 和 按需导入 的区别 区别
1.全部导入会引起项目打包后的体积变大进而影响用户访问网站的性能
2.按需导入只会导入你使用的组件进而节约了资源
八、全部导入
安装vant-ui
yarn add vantlatest-v2
// 或者 npm i vantlatest-v2在main.js中
import Vant from vant;
import vant/lib/index.css;
// 把vant中所有的组件都导入了
Vue.use(Vant)即可使用
van-button typeprimary主要按钮/van-button
van-button typeinfo信息按钮/van-buttonvant-ui提供了很多的组件全部导入会导致项目打包变得很大。
九、按需导入
安装vant-ui
npm i vantlatest-v2 或 yarn add vantlatest-v2安装一个插件
npm i babel-plugin-import -D在babel.config.js中配置
module.exports {presets: [vue/cli-plugin-babel/preset],plugins: [[import, {libraryName: vant,libraryDirectory: es,style: true}, vant]]
}按需加载在main.js
import { Button, Icon } from vantVue.use(Button)
Vue.use(Icon)app.vue中进行测试
van-button typeprimary主要按钮/van-button
van-button typeinfo信息按钮/van-button
van-button typedefault默认按钮/van-button
van-button typewarning警告按钮/van-button
van-button typedanger危险按钮/van-button把引入组件的步骤抽离到单独的js文件中比如 utils/vant-ui.js
import { Button, Icon } from vantVue.use(Button)
Vue.use(Icon)main.js中进行导入
// 导入按需导入的配置文件
import /utils/vant-ui十、项目中的vw适配
官方说明https://vant-contrib.gitee.io/vant/v2/#/zh-CN/advanced-usage
yarn add postcss-px-to-viewport1.1.1 -D项目根目录 新建postcss的配置文件postcss.config.js
// postcss.config.js
module.exports {plugins: {postcss-px-to-viewport: {viewportWidth: 375,},},
};viewportWidth:设计稿的视口宽度
vant-ui中的组件就是按照375的视口宽度设计的恰好面经项目中的设计稿也是按照375的视口宽度设计的所以此时 我们只需要配置375就可以了如果设计稿不是按照375而是按照750的宽度设计那此时这个值该怎么填呢
十一、路由配置-一级路由
但凡是单个页面独立展示的都是一级路由
路由设计
登录页 一级 Login注册页一级 Register文章详情页一级 Detail首页一级 Layout 面经二级Article收藏二级Collect喜欢二级Like我的二级My 一级路由
router/index.js配置一级路由, 一级views组件于准备好的中直接 CV 即可
import Vue from vue
import VueRouter from vue-router
import Login from /views/Login
import Register from /views/Register
import Detail from /views/Detail
import Layout from /views/Layout
Vue.use(VueRouter)const router new VueRouter({routes: [{ path: /login, component: Login },{ path: /register, component: Register },{ path: /article/:id, component: Detail },{path: /,component: Layout}]
})
export default router清理 App.vue
templatediv idapprouter-view//div
/templatescript
export default {created () {}
}
/script十二、路由配置-tabbar标签页 https://vant-contrib.gitee.io/vant/v2/#/zh-CN/tabbar
vant-ui.js 引入组件
import { Button, Icon, Tabbar, TabbarItem } from vant
Vue.use(Tabbar)
Vue.use(TabbarItem)layout.vue
复制官方代码修改显示文本及显示的图标
templatediv classlayout-page首页架子 - 内容区域 van-tabbarvan-tabbar-item iconnotes-o面经/van-tabbar-itemvan-tabbar-item iconstar-o收藏/van-tabbar-itemvan-tabbar-item iconlike-o喜欢/van-tabbar-itemvan-tabbar-item iconuser-o我的/van-tabbar-item/van-tabbar/div
/template十三、路由配置-配置主题色
整体网站风格其实都是橙色的可以通过变量覆盖的方式制定主题色
https://vant-contrib.gitee.io/vant/v2/#/zh-CN/theme
babel.config.js 制定样式路径
module.exports {presets: [vue/cli-plugin-babel/preset],plugins: [[import, {libraryName: vant,libraryDirectory: es,// 指定样式路径style: (name) ${name}/style/less}, vant]]
}vue.config.js 覆盖变量
const { defineConfig } require(vue/cli-service)
module.exports defineConfig({transpileDependencies: true,css: {loaderOptions: {less: {lessOptions: {modifyVars: {// 直接覆盖变量blue: #FA6D1D,},},},},},
})重启服务器生效
十四、路由配置-二级路由 1.router/index.js配置二级路由
在准备好的代码中去复制对应的组件即可
import Vue from vue
import VueRouter from vue-router
import Login from /views/Login
import Register from /views/Register
import Detail from /views/Detail
import Layout from /views/Layoutimport Like from /views/Like
import Article from /views/Article
import Collect from /views/Collect
import User from /views/User
Vue.use(VueRouter)const router new VueRouter({routes: [{ path: /login, component: Login },{ path: /register, component: Register },{ path: /article/:id, component: Detail },{ path: /,component: Layout,redirect: /article,children: [{ path: article, component: Article },{ path: like, component: Like },{ path: collect, component: Collect },{ path: user, component: User }]}]
})export default router2.layout.vue 配置路由出口, 配置 tabbar
templatediv classlayout-page//路由出口router-view/router-view van-tabbar routevan-tabbar-item to/article iconnotes-o面经/van-tabbar-itemvan-tabbar-item to/collect iconstar-o收藏/van-tabbar-itemvan-tabbar-item to/like iconlike-o喜欢/van-tabbar-itemvan-tabbar-item to/user iconuser-o我的/van-tabbar-item/van-tabbar/div
/template十五、登录静态布局 使用组件
van-nav-barvan-formvan-fieldvan-button
vant-ui.js 注册
import Vue from vue
import {NavBar,Form,Field
} from vant
Vue.use(NavBar)
Vue.use(Form)
Vue.use(Field)Login.vue 使用
templatediv classlogin-page!-- 导航栏部分 --van-nav-bar title面经登录 /!-- 一旦form表单提交了就会触发submit可以在submit事件中根据拿到的表单提交信息发送axios请求--van-form submitonSubmit!-- 输入框组件 --!-- \w 字母数字_ \d 数字0-9 --van-fieldv-modelusernamenameusernamelabel用户名placeholder用户名:rules[{ required: true, message: 请填写用户名 },{ pattern: /^\w{5,}$/, message: 用户名至少包含5个字符 }]/van-fieldv-modelpasswordtypepasswordnamepasswordlabel密码placeholder密码:rules[{ required: true, message: 请填写密码 },{ pattern: /^\w{6,}$/, message: 密码至少包含6个字符 }]/div stylemargin: 16pxvan-button block typeinfo native-typesubmit提交/van-button/div/van-form/div
/templatescript
export default {name: LoginPage,data () {return {username: zhousg,password: 123456}},methods: {onSubmit (values) {console.log(submit, values)}}
}
/scriptlogin.vue添加 router-link 标签跳转到注册
templatediv classlogin-pagevan-nav-bar title面经登录 /van-form submitonSubmit.../van-formrouter-link classlink to/register注册账号/router-link/div
/templatelogin.vue调整样式
style langless scoped
.link {color: #069;font-size: 12px;padding-right: 20px;float: right;
}
/style十六、登录表单中的细节分析
submit事件:当点击提交按钮时会自动触发submit事件v-model双向绑定会自动把v-model后面的值和文本框中的值进行双向绑定name属性:收集的key的值要和接口文档对应起来label:输入的文本框的title:rules: 表单的校验规则placeholder: 文本框的提示语
十七、注册静态布局
Register.vue
templatediv classlogin-pagevan-nav-bar title面经注册 /van-form submitonSubmitvan-fieldv-modelusernamenameusernamelabel用户名placeholder用户名:rules[{ required: true, message: 请填写用户名 },{ pattern: /^\w{5,}$/, message: 用户名至少包含5个字符 }]/van-fieldv-modelpasswordtypepasswordnamepasswordlabel密码placeholder密码:rules[{ required: true, message: 请填写密码 },{ pattern: /^\w{6,}$/, message: 密码至少包含6个字符 }]/div stylemargin: 16pxvan-button block typeprimary native-typesubmit注册/van-button/div/van-formrouter-link classlink to/login有账号去登录/router-link/div
/templatescript
export default {name: Register-Page,data () {return {username: ,password: }},methods: {onSubmit (values) {console.log(submit, values)}}
}
/scriptstyle langless scoped
.link {color: #069;font-size: 12px;padding-right: 20px;float: right;
}
/style十八、request模块 - axios封装
接口文档地址https://apifox.com/apidoc/project-934563/api-20384515
基地址http://interview-api-t.itheima.net/h5/
目标将 axios 请求方法封装到 request 模块
我们会使用 axios 来请求后端接口, 一般都会对 axios 进行一些配置 (比如: 配置基础地址,请求响应拦截器等等)
一般项目开发中, 都会对 axios 进行基本的二次封装, 单独封装到一个模块中, 便于使用
安装 axios
npm i axios新建 utils/request.js 封装 axios 模块 利用 axios.create 创建一个自定义的 axios 来使用 http://www.axios-js.com/zh-cn/docs/#axios-create-config
/* 封装axios用于发送请求 */
import axios from axios// 创建一个新的axios实例
const request axios.create({baseURL: http://interview-api-t.itheima.net/h5/,timeout: 5000
})// 添加请求拦截器
request.interceptors.request.use(function (config) {// 在发送请求之前做些什么return config
}, function (error) {// 对请求错误做些什么return Promise.reject(error)
})// 添加响应拦截器
request.interceptors.response.use(function (response) {// 对响应数据做点什么return response.data
}, function (error) {// 对响应错误做点什么return Promise.reject(error)
})export default request注册测试
// 监听表单的提交形参中可以获取到输入框的值
async onSubmit (values) {console.log(submit, values)const res await request.post(/user/register, values)console.log(res)
}十九、封装api接口 - 注册功能
1.目标将请求封装成方法统一存放到 api 模块与页面分离
2.原因 以前的模式
页面中充斥着请求代码可阅读性不高相同的请求没有复用请求没有统一管理
3.期望 请求与页面逻辑分离相同的请求可以直接复用请求进行了统一管理
4.具体实现
新建 api/user.js 提供注册 Api 函数
import request from /utils/request// 注册接口
export const register (data) {return request.post(/user/register, data)
}register.vue页面中调用测试
methods: {async onSubmit (values) {// 往后台发送注册请求了await register(values)alert(注册成功)this.$router.push(/login)}
}二十、toast 轻提示
https://vant-contrib.gitee.io/vant/v2/#/zh-CN/toast
两种使用方式
组件内或js文件内 导入调用
import { Toast } from vant;
Toast(提示内容);**组件内 **通过this直接调用
main.js
import { Toast } from vant;
Vue.use(Toast)this.$toast(提示内容)代码演示
this.$toast.loading({message:拼命加载中...,forbidClick:true
})
try{await register(values)this.$toast.success(注册成功)this.$router.push(/login)
}catch(e){this.$toast.fail(注册失败)
}二十一、响应拦截器统一处理错误提示
响应拦截器是咱们拿到数据的第一个“数据流转站” import { Toast } from vant...// 添加响应拦截器
request.interceptors.response.use(function (response) {// 对响应数据做点什么return response.data
}, function (error) {if (error.response) {// 有错误响应, 提示错误提示Toast(error.response.data.message)}// 对响应错误做点什么return Promise.reject(error)
})二十二、封装api接口 - 登录功能
api/user.js 提供登录 Api 函数
// 登录接口
export const login (data) {return request.post(/user/login, data)
}login.vue 登录功能
import { login } from /api/usermethods: {async onSubmit (values) {const { data } await login(values)this.$toast.success(登录成功)localStorage.setItem(vant-mobile-exp-token, data.token)this.$router.push(/)}
}二十三、local模块 - 本地存储
新建 utils/storage.js
const KEY vant-mobile-exp-token// 直接用按需导出可以导出多个
// 获取
export const getToken () {return localStorage.getItem(KEY)
}// 设置
export const setToken (newToken) {localStorage.setItem(KEY, newToken)
}// 删除
export const delToken () {localStorage.removeItem(KEY)
}
登录完成存储token到本地
import { login } from /api/user
import { setToken } from /utils/storagemethods: {async onSubmit (values) {const { data } await login(values)setToken(data.token)this.$toast.success(登录成功)this.$router.push(/)}
}day2
一、全局前置守卫-语法认识
这个 面经移动端 项目只对 登录用户 开放如果未登录一律拦截到登录 如果访问的是 首页 无token 拦走 如果访问的是 列表页无token 拦走 如果访问的是 详情页无token 拦走 …
分析哪些页面是不需要登录就可以访问的 注册 和 登录 白名单 - 游客可以随意访问的 路由导航守卫 - 全局前置守卫 访问的路径一旦被路由规则匹配到都会先经过全局前置守卫 只有全局前置守卫放行才会真正解析渲染组件才能看到页面内容
router/index.js
router.beforeEach((to, from, next) {// 1. to 往哪里去 到哪去的路由信息对象 // 2. from 从哪里来 从哪来的路由信息对象// 3. next() 是否放行// 如果next()调用就是放行// next(路径) 拦截到某个路径页面
})二、全局前置守卫-访问拦截处理
拦截或放行的关键点 → 用户是否有登录权证 token
核心逻辑
判断用户有没有token 有token 直接放行 有身份的人想去哪就去哪~没有token游客如果是白名单中的页面直接放行否则无token游客且在访问需要权限访问的页面直接拦截到登录 // 全局前置守卫
// 1. 所有的路由一旦被匹配到在真正渲染解析之前都会先经过全局前置守卫
// 2. 只有全局前置守卫放行才能看到真正的页面// 任何路由被解析访问前都会先执行这个回调
// 1. from 你从哪里来 从哪来的路由信息对象
// 2. to 你往哪里去 到哪去的路由信息对象
// 3. next() 是否放行如果next()调用就是放行 放你去想去的页面
// next(路径) 拦截到某个路径页面
import { getToken } from /utils/storageconst whiteList [/login, /register] // 白名单列表记录无需权限访问的所有页面router.beforeEach((to, from, next) {const token getToken()// 如果有token直接放行if (token) {next()} else {// 没有token的人, 看看你要去哪// (1) 访问的是无需授权的页面白名单也是放行// 就是判断访问的地址是否在白名单数组中存在 includesif (whiteList.includes(to.path)) {next()} else {// (2) 否则拦截到登录next(/login)}}
})三、面经列表-认识Cell组件-准备基础布局
1.认识静态结构
2.注册组件
van-cell
import Vue from vue
import { Cell } from vant
Vue.use(Cell)3.静态结构 Article.vue
templatediv classarticle-pagenav classmy-nav van-hairline--bottomahrefjavascript:;推荐/aahrefjavascript:;最新/adiv classlogoimg src/assets/logo.png alt/div/navvan-cell classarticle-item template #titlediv classheadimg srchttp://teachoss.itheima.net/heimaQuestionMiniapp/%E5%AE%98%E6%96%B9%E9%BB%98%E8%AE%A4%E5%A4%B4%E5%83%8F%402x.png alt /div classconp classtitle van-ellipsis宇宙头条校招前端面经/pp classother不风流怎样倜傥 | 2022-01-20 00-00-00/p/div/div/templatetemplate #labeldiv classbody van-multi-ellipsis--l2笔者读大三, 前端小白一枚, 正在准备春招, 人生第一次面试, 投了头条前端, 总共经历了四轮技术面试和一轮hr面, 不多说, 直接上题nbsp;一面/divdiv classfoot点赞 46 | 浏览 332/div/template/van-cell/div
/templatescript
export default {name: article-page,data () {return {}},methods: {}
}
/scriptstyle langless scoped
.article-page {margin-bottom: 50px;margin-top: 44px;.my-nav {height: 44px;position: fixed;left: 0;top: 0;width: 100%;z-index: 999;background: #fff;display: flex;align-items: center; a {color: #999;font-size: 14px;line-height: 44px;margin-left: 20px;position: relative;transition: all 0.3s;::after {content: ;position: absolute;left: 50%;transform: translateX(-50%);bottom: 0;width: 0;height: 2px;background: #222;transition: all 0.3s;}.active {color: #222;::after {width: 14px;}}}.logo {flex: 1;display: flex;justify-content: flex-end; img {width: 64px;height: 28px;display: block;margin-right: 10px;}}}
}
.article-item {.head {display: flex;img {width: 40px;height: 40px;border-radius: 50%;overflow: hidden;}.con {flex: 1;overflow: hidden;padding-left: 10px;p {margin: 0;line-height: 1.5;.title {width: 280px;}.other {font-size: 10px;color: #999;}}}}.body {font-size: 14px;color: #666;line-height: 1.6;margin-top: 10px;}.foot {font-size: 12px;color: #999;margin-top: 10px;}
}
/style四、封装 ArticleItem 组件
说明每个文章列表项其实就是一个整体封装成一个组件 → 可阅读性 复用性
步骤
新建 components/ArticleItem.vue 组件贴入内容注册成全局组件Article.vue 页面中应用
新建 components/ArticleItem.vue 组件
templatevan-cell classarticle-itemtemplate #titlediv classheadimg srchttp://teachoss.itheima.net/heimaQuestionMiniapp/%E5%AE%98%E6%96%B9%E9%BB%98%E8%AE%A4%E5%A4%B4%E5%83%8F%402x.pngalt/div classconp classtitle van-ellipsis宇宙头条校招前端面经/pp classother不风流怎样倜傥 | 2022-01-20 00-00-00/p/div/div/templatetemplate #labeldiv classbody van-multi-ellipsis--l2笔者读大三, 前端小白一枚, 正在准备春招, 人生第一次面试, 投了头条前端,总共经历了四轮技术面试和一轮hr面, 不多说, 直接上题nbsp;一面/divdiv classfoot点赞 46 | 浏览 332/div/template/van-cell
/templatescript
export default {name: ArticleItem
}
/scriptstyle langless scoped
.article-item {.head {display: flex;img {width: 40px;height: 40px;border-radius: 50%;overflow: hidden;}.con {flex: 1;overflow: hidden;padding-left: 10px;p {margin: 0;line-height: 1.5;.title {width: 280px;}.other {font-size: 10px;color: #999;}}}}.body {font-size: 14px;color: #666;line-height: 1.6;margin-top: 10px;}.foot {font-size: 12px;color: #999;margin-top: 10px;}
}
/style
注册成全局组件使用
import ArticleItem from /components/ArticleItem.vue
Vue.component(ArticleItem, ArticleItem)Article.vue页面中
templatediv classarticle-page... ArticleItem/ArticleItem/div
/template五、封装 api 接口-获取文章列表数据
接口https://apifox.com/apidoc/project-934563/api-20384521
1.新建 api/article.js 提供接口函数
import request from /utils/requestexport const getArticles (obj) {return request.get(/interview/query, {params: {current: obj.current,sorter: obj.sorter,pageSize: 10}})
}2.页面中调用测试
import { getArticles } from /api/article
export default {name: article-page,data () {return {}},async created () {const res await getArticles({current: 1,sorter: weight_desc})console.log(res)},methods: {}
}3.发现 401 错误, 通过 headers 携带 token
注意这个token需要拼上前缀 Bearer token标识前缀
// 封装接口获取文章列表
export const getArticles (obj) {const token getToken()return request.get(/interview/query, {params: {current: obj.current, // 当前页pageSize: 10, // 每页条数sorter: obj.sorter // 排序字段 传weight_desc 获取 推荐 不传 获取 最新},headers: {// 注意 Bearer 和 后面的空格不能删除为后台的token辨识Authorization: Bearer ${token}}})
}六、请求拦截器-携带 token
utils/request.js 每次自己携带token太麻烦通过请求拦截器统一携带token更方便
import { getToken } from ./storage// 添加请求拦截器
request.interceptors.request.use(function (config) {// 在发送请求之前做些什么const token getToken()if (token) {config.headers.Authorization Bearer ${token}}return config
}, function (error) {// 对请求错误做些什么return Promise.reject(error)
})七、响应拦截器-处理token过期
说明token 是有过期时间的 (6h)一旦 过期 或 失效 就无法正确获取到数据 utils/request.js
// 添加响应拦截器
request.interceptors.response.use(function (response) {// 对响应数据做点什么return response.data
}, function (error) {if (error.response) {// 有错误响应, 提示错误提示if (error.response.status 401) {delToken()router.push(/login)} else {Toast(error.response.data.message)}}// 对响应错误做点什么return Promise.reject(error)
})八、面经列表-动态渲染列表 article.vue
存储数据
import {getArticles} from /api/article
data () {return {list: [],current: 1,sorter: weight_desc}
},
async created () {const { data } await getArticles({current: this.current,sorter: this.sorter})this.list data.data.rows
},v-for循环展示
templatediv classarticle-page...ArticleItem v-for(item,i) in list :keyitem.id :itemitem/ArticleItem/div
/template子组件接收渲染
templatevan-cell classarticle-item click$router.push(/detail/${item.id})template #titlediv classheadimg :srcitem.avatar alt /div classconp classtitle van-ellipsis{{ item.stem }}/pp classother{{ item.creator }} | {{ item.createdAt }}/p/div/div/templatetemplate #labeldiv classbody van-multi-ellipsis--l2 v-htmlitem.content/divdiv classfoot点赞 {{ item.likeCount }} | 浏览 {{ item.views }}/div/template/van-cell
/templatescript
export default {name: ArticleItem,props: {item: {type: Object,default: () ({})}}
}
/scriptstyle langless scoped
.article-item {.head {display: flex;img {width: 40px;height: 40px;border-radius: 50%;overflow: hidden;}.con {flex: 1;overflow: hidden;padding-left: 10px;p {margin: 0;line-height: 1.5;.title {width: 280px;}.other {font-size: 10px;color: #999;}}}}.body {font-size: 14px;color: #666;line-height: 1.6;margin-top: 10px;}.foot {font-size: 12px;color: #999;margin-top: 10px;}
}
/style九、面经列表-响应拦截器-简化响应
// 添加响应拦截器
instance.interceptors.response.use(function (response) {// 对响应数据做点什么return response.data
}, function (error) {// console.log(error)// 有错误响应后台正常返回了错误信息if (error.response) {if (error.response.status 401) {// 清除掉无效的tokendelToken()// 拦截到登录router.push(/login)} else {// 有错误响应提示错误消息// this.$toast(error.response.data.message)Toast(error.response.data.message)}}// 对响应错误做点什么return Promise.reject(error)
})Login.vue
setToken(data.token)Article.vue
async created () {// 获取推荐的第1页的10条数据const res await getArticles({current: this.current,sorter: this.sorter})this.list res.data.rows
},十、面经列表-分页加载更多
https://vant-contrib.gitee.io/vant/v2/#/zh-CN/list
van-listv-modelloading:finishedfinishedfinished-text没有更多了loadonLoad
ArticleItem v-for(item,i) in list :keyi :itemitem/ArticleItem
/van-listdata () {return {list: [],current: 1,sorter: weight_desc,loading: false,finished: false}
},methods: {async onLoad () {const { data } await getArticles({current: this.current,sorter: this.sorter})this.list data.rows}
}加载完成重置 loading, 累加数据处理 finished
async onLoad () {const { data } await getArticles({current: this.current,sorter: this.sorter})this.list.push(...data.rows)this.loading falsethis.currentif (this.current data.pageTotal) {this.finished true}
}十一、面经列表-推荐和更新
1.切换推荐和最新 获取不同的数据
2.切换推荐和最新 点击的tab页签应该高亮 article.vue
aclickchangeSorter(weight_desc):class{ active: sorter weight_desc }hrefjavascript:;推荐/aaclickchangeSorter(null):class{ active: sorter null }hrefjavascript:;最新/a提供methods
changeSorter (value) {this.sorter value// 重置所有条件this.current 1 // 排序条件变化重新从第一页开始加载this.list []this.finished false // finished重置重新有数据可以加载了// this.loading false// 手动加载更多// 手动调用了加载更多也需要手动将loading改成true表示正在加载中避免重复触发this.loading truethis.onLoad()
}十二、面经详情-动态路由传参-请求渲染
1.跳转路由传参
核心知识点跳转路由传参
准备动态路由 (已准备)
const router new VueRouter({routes: [...,{ path: /article/:id, component: Detail },{path: /,component: Layout,redirect: /article,children: [...]}]
})点击跳转 article.vue
template!-- 文章区域 --van-cell classarticle-item click$router.push(/detail/${item.id})template #title.../templatetemplate #label.../template/van-cell
/template页面中获取参数
this.$route.params.id2.动态渲染 (页面代码准备)
准备代码
导入图标组件
Vue.use(Icon)静态结构
templatediv classdetail-pagevan-nav-barleft-text返回click-left$router.back()fixedtitle面经详情/header classheaderh1大标题/h1p2050-04-06 | 300 浏览量 | 222 点赞数/ppimg src头像 alt /span作者/span/p/headermain classbodyp我是内容/pp我是内容/pp我是内容/pp我是内容/p/maindiv classoptvan-icon classactive namelike-o/van-icon namestar-o//div/div
/templatescript
export default {name: detail-page,data () {return {article: {}}},async created () {},methods: {}
}
/scriptstyle langless scoped
.detail-page {margin-top: 44px;overflow: hidden;padding: 0 15px;.header {h1 {font-size: 24px;}p {color: #999;font-size: 12px;display: flex;align-items: center;}img {width: 40px;height: 40px;border-radius: 50%;overflow: hidden;}}.opt {position: fixed;bottom: 100px;right: 0; .van-icon {margin-right: 20px;background: #fff;width: 40px;height: 40px;line-height: 40px;text-align: center;border-radius: 50%;box-shadow: 2px 2px 10px #ccc;font-size: 18px;.active {background: #FEC635;color: #fff;}}}
}
/style
3.代码实现
3.1封装api接口函数
api/article.js
export const getArticleDetail (id) {return request.get(interview/show, {params: {id}})
}3.2动态渲染
Detail.vue
templatediv classdetail-pagevan-nav-barleft-text返回click-left$router.back()fixedtitle面经详细/header classheaderh1{{ article.stem }}/h1p{{ article.createdAt }} | {{ article.views }} 浏览量 |{{ article.likeCount }} 点赞数/ppimg :srcarticle.avatar alt /span{{ article.creator }}/span/p/headermain classbody v-htmlarticle.content/maindiv classoptvan-icon :class{active:article.likeFlag} namelike-o/van-icon :class{active:article.collectFlag} namestar-o//div/div
/templatescript
import { getArticleDetail } from /api/articleexport default {name: detail-page,data () {return {article: {}}},async created () {this.article {}const { data } await getArticleDetail(this.$route.params.id)this.article data},methods: {}
}
/script十三、面经详情-点赞收藏
封装准备接口
api/article.js
export const updateLike (id) {return request.post(interview/opt, {id,optType: 1 // 喜欢})
}export const updateCollect (id) {return request.post(interview/opt, {id,optType: 2 // 收藏})
}Detail.vue
调用接口实现点赞收藏
templatediv classdetail-pagevan-nav-barleft-text返回click-left$router.back()fixedtitle面经详细/header classheaderh1{{ article.stem }}/h1p{{ article.createdAt }} | {{ article.views }} 浏览量 |{{ article.likeCount }} 点赞数/ppimg :srcarticle.avatar alt /span{{ article.creator }}/span/p/headermain classbody v-htmlarticle.content/maindiv classoptvan-icon clicktoggleLike :class{active:article.likeFlag} namelike-o/van-icon clicktoggleCollect :class{active:article.collectFlag} namestar-o//div/div
/templatescript
import { getArticleDetail, updateCollect, updateLike } from /api/article;export default {name: detail-page,data() {return {article: {}};},async created() {this.article {}const { data } await getArticleDetail(this.$route.params.id)this.article data;},methods: {async toggleLike () {await updateLike(this.article.id)this.article.likeFlag !this.article.likeFlagif ( this.article.likeFlag ) {this.article.likeCount this.$toast.success(点赞成功)} else {this.article.likeCount --this.$toast.success(取消点赞)}},async toggleCollect () {await updateCollect(this.article.id)this.article.collectFlag !this.article.collectFlagif ( this.article.collectFlag ) {this.$toast.success(收藏成功)} else {this.$toast.success(取消收藏)}}}
};
/scriptstyle langless scoped
.detail-page {margin-top: 44px;overflow: hidden;padding: 0 15px;.header {h1 {font-size: 24px;}p {color: #999;font-size: 12px;display: flex;align-items: center;}img {width: 40px;height: 40px;border-radius: 50%;overflow: hidden;}}.opt {position: fixed;bottom: 100px;right: 0; .van-icon {margin-right: 20px;background: #fff;width: 40px;height: 40px;line-height: 40px;text-align: center;border-radius: 50%;box-shadow: 2px 2px 10px #ccc;font-size: 18px;.active {background: #FEC635;color: #fff;}}}
}
/style十四、我的收藏 (实战)
提供api方法
page 表示当前页optType2 表示获取我的收藏数据
api/article.js
// 获取我的收藏
export const getArticlesCollect (obj) {return request.get(/interview/opt/list, {params: {page: obj.page, // 当前页pageSize: 5, // 可选optType: 2 // 表示收藏}})
}collect.vue准备结构
templatediv classcollect-pagevan-nav-bar fixed title我的收藏 /van-listv-modelloading:finishedfinishedfinished-text没有更多了loadonLoadArticleItem v-for(item, i) in list :keyi :itemitem //van-list/div
/templatescript
import { getArticlesCollect } from /api/article
export default {name: collect-page,data () {return {list: [],loading: false,finished: false,page: 1}},methods: {async onLoad () {// 异步更新数据const { data } await getArticlesCollect({ page: this.page })this.list.push(...data.rows)this.loading falsethis.pageif (this.page data.pageTotal) {this.finished true}}}
}
/scriptstyle langless scoped
.collect-page {margin-bottom: 50px;margin-top: 44px;
}
/style十五、我的喜欢 (快速实现)
准备api函数
page 表示当前页optType1 表示获取我的喜欢数据
api/article.js
// 获取我的喜欢
export const getArticlesLike (obj) {return request.get(/interview/opt/list, {params: {page: obj.page, // 当前页pageSize: 5, // 可选optType: 1 // 表示喜欢}})
}Like.vue请求渲染
templatediv classlike-pagevan-nav-bar fixed title我的点赞 /van-listv-modelloading:finishedfinishedfinished-text没有更多了loadonLoadArticleItem v-for(item,i) in list :keyi :itemitem //van-list/div
/templatescript
import { getArticlesLike } from /api/article
export default {name: like-page,data () {return {list: [],loading: false,finished: false,page: 1}},methods: {async onLoad () {// 异步更新数据const { data } await getArticlesLike({ page: this.page })this.list.push(...data.rows)this.loading falsethis.pageif (this.page data.pageTotal) {this.finished true}}}
}
/scriptstyle langless scoped
.like-page {margin-bottom: 50px;margin-top: 44px;
}
/style十六、个人中心 (快速实现)
准备代码
1 注册组件
import {Grid,GridItem,CellGroup
} from vantVue.use(Grid)
Vue.use(GridItem)
Vue.use(CellGroup)2 准备api
api/user.js
// 获取用户信息
export const getUserInfo () {return request(/user/currentUser)
}3 页面调用渲染
templatediv classuser-pagediv classuserimg :srcavatar alt /h3{{ username }}/h3/divvan-grid clickable :column-num3 :borderfalsevan-grid-item iconclock-o text历史记录 to/ /van-grid-item iconbookmark-o text我的收藏 to/collect /van-grid-item iconthumb-circle-o text我的点赞 to/like //van-gridvan-cell-group classmt20van-cell title推荐分享 is-link /van-cell title意见反馈 is-link /van-cell title关于我们 is-link /van-cell clicklogout title退出登录 is-link //van-cell-group/div
/templatescript
import { getUserInfo } from /api/user
import { delToken } from /utils/storage
export default {name: user-page,data () {return {username: ,avatar: }},async created () {const { data } await getUserInfo()this.username data.usernamethis.avatar data.avatar},methods: {logout () {delToken()this.$router.push(/login)}}
}
/scriptstyle langless scoped
.user-page {padding: 0 10px;background: #f5f5f5;height: 100vh;.mt20 {margin-top: 20px;}.user {display: flex;padding: 20px 0;align-items: center;img {width: 80px;height: 80px;border-radius: 50%;overflow: hidden;}h3 {margin: 0;padding-left: 20px;font-size: 18px;}}
}
/style十七、打包发布
vue脚手架只是开发过程中协助开发的工具当真正开发完了 脚手架不参与上线
参与上线的是 打包后的源代码
打包
将多个文件压缩合并成一个文件语法降级less sass ts 语法解析, 解析成css…
打包后可以生成浏览器能够直接运行的网页 就是需要上线的源码
打包命令
vue脚手架工具已经提供了打包命令直接使用即可。
yarn build在项目的根目录会自动创建一个文件夹dist,dist中的文件就是打包后的文件只需要放到服务器中即可。
配置publicPath
module.exports {// 设置获取.js,.css文件时是以相对地址为基准的。// https://cli.vuejs.org/zh/config/#publicpathpublicPath: ./
}十八、路由懒加载
路由懒加载 异步组件 不会一上来就将所有的组件都加载而是访问到对应的路由了才加载解析这个路由对应的所有组件
官网链接https://router.vuejs.org/zh/guide/advanced/lazy-loading.html#%E4%BD%BF%E7%94%A8-webpack 当打包构建应用时JavaScript 包会变得非常大影响页面加载。如果我们能把不同路由对应的组件分割成不同的代码块然后当路由被访问的时候才加载对应组件这样就更加高效了。 const Detail () import(/views/detail)
const Register () import(/views/register)
const Login () import(/views/login)
const Article () import(/views/article)
const Collect () import(/views/collect)
const Like () import(/views/like)
const User () import(/views/user)PS 如果想要手机上看到效果可以将打包后的代码上传到 gitee利用 git pages 进行展示