一、目标
- vue/vuex实战
- axios二次配置
intersectionObserver终极应用
实现图片懒加载、实现数据的下拉刷新
骨架屏Skeleton技术
- 使用Object.freeze冻结数据,提高性能,不用去拦截get、set方法
- 使用koa,node服务提供web API
二、实现
1、vue.config.js 配置
module.exports = {lintOnSave:false,devServer:{// 跨域请求:PROXY代理proxy:{'/':{target:"http://127.0.0.1:8000",changeOrigin:true}}}}
2、 动态计算rem
index.html
<script>(function() {function computed() {let HTML = document.documentElement,winW = HTML.clientWidth,desW = 750;if (winW >= desW) {HTML.style.fontSize = '100px';return;}HTML.style.fontSize = 100 * (winW / desW) + 'px';}computed();window.addEventListener('resize', computed);})();</script>
3、main.js
import Vue from 'vue';import App from './App.vue';import api from './api/index';import router from './router/index';Vue.config.productionTip = false;Vue.prototype.$api = api; // this.$api.xxx()new Vue({router,render: (h) => h(App),}).$mount('#app');
4、 App.vue
<template><div id="app"><heade></heade><router-view></router-view></div></template><script>import heade from './components/Header';export default {name: 'App',components: {heade,},created() {},};</script><style>* {padding: 0;margin: 0;font-size: 0.24rem;}</style>
5、 router配置
src/router.index.js
import Vue from 'vue'import VueRouter from 'vue-router';/*导入需要渲染的组件*/import Home from "../view/Home.vue"import Detail from "./view/Detail.vue"Vue.use(VueRouter)const router = new VueRouter({mode:'hash',routes:[{path:'/',name:'home',component:Home},{path:'/',name:'detail',component:Detail},{path:'*',redirect:'/'}]})export default router;
路由面试相关问题
1、路由的模块开发
2、路由懒加载 原理 通过webpack 分块加载
3、路由传参
4、路由守卫 => 权限校验的N种方案
5、HASH/BEOWSER路由原理和区别
…
6、封装axios
fetch脱离了xml
http.js
封装axios
src/api/http.js
import axios from 'axios';import qs from 'qs';axios.defaults.baseURL = "";axios.defaults.withCredentials = true; // 允许跨域携带资源凭证axios.defaults.headers['Content-Type'] = 'application/x-www-form-urlencoded'axios.defaults.transformRequest=data=>qs.stringify(data) // post => xxx=xxxaxios.defaults.timeout = 0; // 超时时间// 请求拦截器 客户端向服务端发送之前axios.interceptors.request.use(config=>{// config 包含所有请求的配置信息// config.headers['X-token'] = 'xxx'// 把token信息发给服务器return config})// 响应拦截器,从服务器获取的结果进行统一处理axios.interceptors.response.use(response=>{// 处理成功,默认认为状态码以2开头return response.data},reason=>{// 失败 1、获取了结果但是状态码不是2开头 2、压根没有从服务器获取数据if(reason.response){// 获取到数据了,根据不同状态码做不同提示}else{// 没有获取数据if(!window.navigator.onLine){// 断网}}return Promise.reject(reason)})// axios.defaults.validate...export default axios;
src/api/index.js
import axios from './http'function QUERY_LIST(){return axois.get('/list')}export default {QUERY_LIST}
7、 首页
view/Home.vue
<template><div class="homeBox"><Skeleton v-if="list.length === 0"></Skeleton><div class="content" v-else><Item v-for="(item, index) in list" :key="index" :data="item" /></div><div ref="loadMore">{{ list.length > 0 ? 'loadMore加载更多。。。' : '' }}</div></div></template><script>import Skeleton from '../components/Skeleton';import Item from '../components/Item';import Util from '../util/index';export default {name: 'Home',components: {Skeleton,Item,},data() {return {list: [],// has_more: false,};},created() {this.fetchList();},mounted() {let ob = new IntersectionObserver((changes) => {changes.forEach((item) => {if (item.isIntersecting && this.list.length > 0) {this.fetchList();}});},{// 控制元素出现的时机出触发函数 0 是视图中只要有出现在视图中 0.5 一半时 1是完全在视图中触发threshold: [0],});ob.observe(this.$refs.loadMore);},methods: {async fetchList() {await Util.sleep(2000); // 等待看效果let result = await this.$api.QUERY_LIST();this.has_more = result.has_more;this.list = Object.freeze([...this.list, ...result.data]);},},};</script><style scoped>.homeBox {text-align: center;}.content {padding: 0 0.3rem;}</style>
8、组件划分
- 组件划分
- 组件库的二次封装
- 自己单独封装组件、构建敏捷化平台、如何封装打造开源级
component/Header.vue
<template><div class="abs_m">今日头条</div></template><script>export default {name: 'heade',};</script><style scoped>.abs_m {line-height: 88px;width: 100%;text-align: center;font-size: 20px;color: #fff;font-weight: bold;line-height: 44px;background: #d43d3d;}</style>
component/Item.vue
<template><div><router-linkv-if="data.image_url":to="{ path: `/detail/${data.item_id}` }":class="['item', data.image_url ? 'itemA' : 'itemB']"><div class="text"><p>{{ data.title }}</p><span>{{ data.media_name }}</span></div><div class="img_box" ref="lazyImg"><img src :data-img="data.image_url" alt /></div></router-link><a href v-if="Array.isArray(data.image_list) && data.image_list.length > 0"><div class="text"><p>{{ data.title }}</p><span>{{ data.media_name }}</span></div><div class="imageList"><divclass="img_box"ref="lazyImg"v-for="(item, index) in data.image_list":key="index"><img :data-img="item.url" src="" alt="" /></div></div><span class="mark">央视新闻</span></a></div></template><script>export default {props: {data: {type: Object,required: true,},},mounted() {let ob = new IntersectionObserver((changes) => {changes.forEach((item) => {if (item.isIntersecting) {let target = item.target;this.lazyImg(target);ob.unobserve(target);}});},{// 控制元素出现的时机出触发函数 0 是视图中只要有出现在视图中 0.5 一半时 1是完全在视图中触发threshold: [1],});let lazyImg = this.$refs.lazyImg;Array.isArray(lazyImg)? lazyImg.forEach((el) => {ob.observe(el);}): lazyImg && ob.observe(lazyImg);},methods: {lazyImg(imgBox) {let img = imgBox.querySelector('img'),true_img = img.getAttribute('data-img');img.onload = () => {img.style.opacity = 1;};img.src = true_img;img.removeAttribute('data-img');},},};</script><style lang="less" scoped>a {display: block;color: #000;}img {opacity: 0;transition: opacity 0.3s;}.img_box {display: block;background: #eee;}.imageList {width: 100%;display: flex;overflow: hidden;justify-content: center;.img_box {width: 33%;}}.itemA {display: flex;justify-content: space-between;align-items: center;.text {width: 60%;p {}}.img_box {width: 2.2rem;height: 1.47rem;img {width: 100%;height: 100%;}}}</style>
使用Object.freeze冻结数据,提高性能,不用去拦截get、set方法
通过data-img存一下url,通过出现在视图中把图片地址赋给src
keep-alive 性能优化列表, 返回列表的时候不重新渲染页面
9、server接口服务
server/app.js
// 参考:https://www.cnblogs.com/chanwahfung/p/11415675.htmlvar Koa = require('koa');var Router = require('koa-router');var bodyParser = require('koa-bodyparser');var fs = require('fs');const cors = require('koa2-cors');var app = new Koa();var router = new Router();// 设置头部信息app.use(cors({origin: function(ctx) {return '*'; //cors},exposeHeaders: ['WWW-Authenticate', 'Server-Authorization'],maxAge: 5,credentials: true,allowMethods: ['GET', 'POST'],allowHeaders: ['Content-Type', 'Authorization', 'Accept'],}));// 1. fs文件模块读取routes文件夹目录内容(获得的是一个文件名的数组)// 2. 数组遍历,引入接口文件,将文件名作为路由名,注册使用路由// 3. 使用: /list/getListlet urls = fs.readdirSync(__dirname + '/routes');urls.forEach((element) => {//routes里的js接口文件let module = require(__dirname + '/routes/' + element);//routes里的文件名作为 路由名router.use('/' + element.replace('.js', ''), module.routes());});//使用路由中间件app.use(router.routes()).use(router.allowedMethods()).use(bodyParser());app.listen(8000);console.log('监听8000');
server/routes/list.js
var Router = require('koa-router');var router = new Router();router.get('/getList', (ctx, next) => {let id = ctx.request.params.id;ctx.body = {id,code: 1,};});router.post('/submit', (ctx) => {ctx.body = {code: 1,postParams: ctx.request.body,};});module.exports = router;
const Router = require('koa-router')const route = new Router()const jwt = require('jsonwebtoken')route.get('/getToken', async (ctx)=>{let {name,id} = ctx.queryif(!name && !id){ctx.body = {msg:'不合法',code:0}return}//生成tokenlet token = jwt.sign({name,id},'secret',{ expiresIn: '1h' })ctx.body = {token: token,code:1}})route.get('/getUser', async ctx=>{let id = ctx.query.idctx.body = {user:ctx.payload,id,code:1}})route.get('/getAllUser', async ctx=>{let type = ctx.query.typeif(type){ctx.body = {type,code:1}}else{ctx.body = {msg:'缺少参数type',code:0}}})module.exports = route

