退出运行ctrl+c 大量替换ctrl+f ``反引号:英文输入按钮【~`】
脚手架
脚手架下载/创建项目:
vue init webpack 项目的名字
vue create 项目名称
脚手架目录:
node_modules:放置项目依赖的地方 public:一般放置一些共用的静态资源,打包上线的时候,public文件夹里面资源原封不动打包到dist文件夹里面(不会当做模块放进去) src:程序员源代码文件夹 assets文件夹:经常放置一些静态资源(图片),assets文件夹里面资源webpack会进行打包为一个模块(js文件夹里面) components文件夹:一般放置非路由组件(或者项目共用的组件) App.vue 唯一的根组件,vue中组件全叫.vue main.js 入口文件【程序最先执行的文件】 babel.config.js:babel配置文件 package.json:看到项目描述、项目依赖、项目运行指令 README.md:项目说明文件
项目运行:npm run serve
运行项目时:npm run serve 若运行失败,可能是得把根目录换一下。cd app 进入app文件夹
项目配置:
//在package.json文件中
"scripts": {
"serve": "vue-cli-service serve --open",
"build": "vue-cli-service build",
"lint": "vue-cli-service lint"
}
"serve": "vue-cli-service serve --open --host=localhost",改成这个版本,cli5版本默认运行上面的代码是0.0.0.0:8080
src文件夹的别名的设置(设为@):因为项目大的时候src(源代码文件夹)里面目录会很多,找文件不方便,设置src文件夹的别名的好处,找文件会方便一些
//创建jsconfig.json文件
{
"compilerOptions": {
"baseUrl": "./",
"paths": {
"@/*": [
"src/*"
]
}
},
"exclude": [
"node_modules",
"dist"
]
}
node_modules文件不能使用别名
路由
官方页面:router.vuejs.org/zh/introduc… 路由分析:
- 两个非路由组件:Header 、Footer
- 路由组件:Home、Search、Login(没有底部的Footer组件,带有二维码的)、Register(没有底部的Footer组件,带二维码的)
创建非路由组件
非路由组件使用分为几步: 第一步:定义 xxx.vue 第二步:引入 import 第三步:注册 compoments里面注册 第四步:使用 template里使用
less语法转换为CSS语法
项目采用的less样式,浏览器不识别less语法,需要一些loader进行处理,把less语法转换为CSS语法
- 安装
less less-loader@5
:切记 less-loader 安装版本,不要安装最新版本,安装最新版本 less-loader 会报错,报的错误 setOption 函数未定义
npm install --save less less-loader@6
- 组件识别less组件,需要在style标签的身上加上lang="less",不添加样式不生效:
<style scoped lang="less">
创建路由组件
- 安装路由(对应vue2版本):
cnpm install --save vue-router@3
(save:可以让你安装的依赖,在package.json文件当中进行记录) - 创建路由组件【一般放在views|pages文件夹】
配置路由
router/index.js: 1、在配置路由的地方引入插件 Vue、VueRouter 2、使用插件 Vue.use(VueRouter) 3、引入路由组件 import 4、配置路由 new VueRouter({ routes:[{ path component }] })
注意配置路由那里要写routes,component没有s!
main.js:
1、引入路由@/router
2、注册路由【router小写】
3、路由组件出口地方 App.vue :<router-view>
route与router区别:
$route
:一般获取路由信息【路径、query、params...】$router
:一般进行编程式导航进行路由跳转【push|replace】
路由组件与非路由组件区别
1.路由组件【一般放在 views | pages 文件夹】,非路由组件【一般放 components 文件夹】
2.路由组件一般在 router 文件夹中注册(使用组件的名字)在App.vue里在路由出口的地方显示,非路由组件在使用的时候,一般以标签的形式使用
3.注册完路由,不管路由还是非路由组件,身上都拥有$route
,$router
属性
重定向,在项目跑起来的时候,访问/,定向到首页
path:"*",redirect:"/home"
路由的跳转
- 声明式导航A->B:把a标签改为router-link,务必要有to属性
- 编程式导航:push||replace(还能做一些其他的业务逻辑),绑定
@click
等,后在methods
内定义回调函数【this.$router.push('/...')
】 - 编程式导航更好用:因为可以书写自己的业务逻辑
v-show
| v-if
(项目优化)
- Footer组件显示|隐藏
- 路由元信息:配置路由时,可以给路由添加路由元信息【meta】,但是路由元的key是固定的,不能自己定义。在component里加meta
v-show与v-if区别?(面试) v-show:通过样式display控制 v-if:通过元素上树与下树进行操作
路由传参
- params参数:配置路由需要占位,属于路径URL当中一部分
- query参数:配置路由不需要占位,写法类似于ajax当中query参数
占位!!命名!!
路由传递参数方法:
- 字符串形式,注意去【占位】(占位:
path:"/search/:keyword"
)
this.$router.push('/search/'+ this.keyword+ '?k='+ this.keyword.toUpperCase());
- 模板字符串
this.$router.push(`/search/${this.keyword}?k=${this.keyword.toUpperCase()}`)
- 对象【要给路由命名search】
this.$router.push({name:"search",params:{keyword:this.keyword},query:{k:this.keyword.toUpperCase()}})
路由传递参数相关面试题 1:路由传递参数(对象写法)path是否可以结合params参数一起使用? 不可以:不能这样书写,程序会崩掉。路由跳转传参时,对象写法可以是name、path形式,但path不能与params参数一起使用 2:如何指定params参数可传可不传? 比如:配置路由是已经占位了(params),但是路由跳转的时候不传递,路径会出现问题 正常:
http://localhost:8081/#/search/dfcsa?k=DFCSA
不传params:http://localhost:8081/#/?k=DFCSA
【search没有跳转】 配置路由的时候,占位后面加上一个‘?’【params可以传递也可以不传递】 3:params参数可以传递也可以不传递,但是如果传递是空串,如何解决? 如果指定name与params配置, 但params中数据是一个"", 无法跳转,路径会出问题 使用undefined解决:params参数可以传递也可以不传递(空字符串)params:{keyword:' '||undefined}
4: 路由组件能不能传递props数据? 能,可以不用占位符,可以直接用{{keyword}}显示, (1)vue里:props:['keyword','a','b','k']
(2)路由里:
1.布尔值写法:params //props:true, 2.对象写法:额外的给路由组件传递一些props //props:{a:1,b:2} 3.函数写法:可以把params
、query
参数,通过props
传递给路由组件
props:($route)=>{
return{keyword:$route.params.keyword,k:$route.query.k};
}
//简写:
props:($route)=>({keyword:$route.params.keyword,k:$route.query.k});
编程式导航路由跳转到当前路由(参数不变), 多次执行会抛出NavigationDuplicated的警告错误? 注意:编程式导航(push|replace)才会有这种情况的异常,声明式导航是没有这种问题,因为声明式导航内部已经解决这种问题。这种异常,对于程序没有任何影响的。 原因:由于vue-router最新版本3.5.2,引入了promise,当传递参数多次且重复,会抛出异常,因此出现上面现象。 第一种解决方案:是给push函数,传入相应的成功的回调与失败的回调()=>{},()=>{} (暂时的)
概念:
this:当前组件实例(search)
this.$router
属性:当前属性,属性值VueRouter类的一个实例,在入口文件注册路由时给组件实例添加$router
|$route
属性
function VueRouter(){
}
//原型对象的方法
VueRouter.prototype.push = function(){
//函数的上下文为VueRouter类的一个实例
}
let $router = new VueRouter();
$router.push(xxx);
this.$router.push(xxx);
函数上下文即为this 意思是构造函数
VueRouter
内的this
指向实例对象$router
=即当new VueRouter
时,通过$router
调用push
时,this就是$router
实例。 =所以对原型方法push
进行修改,修改结果就会作用于组件实例的$router
实例。 =说白了就是push
不是$router
本来就带着的方法,而是原型上的,所以要统一修改的话必须从原型上下手而不是$router
//重写push|replace
//push
let originPush = VueRouter.prototype.push;
VueRouter.prototype.push = function(location,resolve,reject){
//参数一:告诉原来push往哪里跳转(传递什么参数)
if(resolve && reject){
originPush.call(this,location,resolve,reject);
}else{
originPush.call(this,location,()=>{},()=>{});
}
}
//replace也一样
1、这是js文件不是vue组件,所以let定义的origin的this指向是window,利用call改this,originPush.call(this) 2、通过call和apply调用的函数,可以篡改函数的上下文(this)一次,它们的第一个参数就是函数的this,call传递参数用逗号隔开,apply方法传递数组
错误提示:
Refused to apply style from 'http://localhost:8081/iconfont.css' because its MIME type ('text/html') is not a supported stylesheet MIME type, and strict MIME checking is enabled.
解决:删除reset.css里的@import "./iconfont.css";
警告:
DevTools failed to load source map: Could not load content for chrome extension://gighmmpiobklfepjocnamgkkbiglidom/browser-polyfill.js.map: System error: net::ERR_FILE_NOT_FOUND
解决:Chrome浏览器的配置问题(与我们的项目配置无关),设置里禁用Enable JavaScript source maps和Enable Css source maps就行
全局组件注册:
把三级联动注册为全局组件:
- 在src/components或pages文件夹下新建 xxx文件夹,xxx文件夹下新建index.vue
- 在main.js中引入,注册为全局组件。
- 使用组件(组件已经注册为全局组件了,不用再引入三级联动组件)
//引入全局组件
import TypeNav from '@/pages/Home/TypeNav'
//注册为全局组件,第一个参数:全局组件的名字,第二个参数:哪个组件
Vue.component(TypeNav.name,TypeNav)
axios二次封装
XMLHttpRequest、$、fetch、axios、JQ
- 为什么需要二次封装axios? 请求拦截器:可以在发送请求之前处理一些业务 响应拦截器:当服务器数据返回以后,可以处理一些事情
安装axios:npm install --save axios
,在package.json里看dependencies有没有安装成功
axios 经常放在 src文件夹下api文件夹,接口中:路径都带/api
api/request.js:在里面对axios进行二次封装
- import引入
- 利用axios对象的方法create,创建一个axios实例, request就是axios(配置了一下)基础路径baseURL:"/api" 请求超过的时间timeout:5000
- 请求拦截器xxx.interceptors.request.use((config)=>{return config;}) //config:配置对象,内有一重要属性:header请求头
- 响应拦截器xxx.interceptors.response.use( (res)=>{return res.data;//成功回调函数},(error)=>{return Promise.reject(new Error('faile'));//响应失败回调函数},)
- 对外暴露export
axios发请求返回结果是Promise对象 发请求的时候由于之前写了baseURL,所以不用再带/api
api/index.js:接口统一管理 1、项目小:在组件生命周期中发请求 2、项目大:axios.get('xxx')
跨域问题:协议、域名、端口号不同请求
JSONP、CROS、代理 http://localhost:8080/#/home ----前端项目本地服务器 http://39.98.123.211 ----后台服务器 //gmall-h5-api.atguigu.cn ----现后台服务器
三级联动的api准备
/api/product/getBaseCategoryList get 无参数
- 对外暴露一个函数(别的模块用)export,在函数里发送请求
- 去入口文件main.js测试一下
- 运行项目,在控制台 network 看是否请求到
- network 里对应函数报 200 就是成功
可以看 wabpack 代理 devServe 相关指导说明 wabpack.config.js=vue.config.js
初始接口报错原因:函数返回值错误
export const reqCategoryList = () => {
//发请求:axios发请求返回Promise对象
//baseURL已经加上了/api,默认返回值是undefined,要把结果返回
return requests({url: '/product/getBaseCategoryList', method: 'get'});
}
//return一开始被忽略,导致返回值undefined,接口报错
//简写
export const reqCategoryList = () => requests({url: '/product/getBaseCategoryList', method: 'get'});
报错403 接口过期:改成了gmall-h5-api.atguigu.cn就可以 服务器与服务器之间是没有跨域问题的,浏览器才有 配置文件配置完要重新运行
进度条功能
nprogress模块实现
工作的时候,修改进度条的颜色,修改源码样式.bar类名的
安装npm i --save nprogress
- 引入进度条 start 开始 done 结束
- 引入进度条样式 nprogresss
vuex
vuex:Vue官方提供的一个插件,状态管理库,集中式管理项目中组件共用数据。项目大的时候,需要有一个地方‘统一管理数据’即为仓库 store。
- 安装:
npm i --save vuex@3
(不安装3版本会报错) - Vuex核心概念:state、actions、mutations、getters、modules state:仓库存储数据的地方 mutations:修改state的唯一手段 actions:处理action,可以书写自己的业务逻辑,也可以处理异步 getters:计算属性,简化仓库数据,让组件获取仓库的数据更方便 modules:模块式开发
- 基本使用:src/store/index.js 1.引入Vue Vuex 2.使用插件一次use 3.对外暴露store类的一个实例 4.入口引入仓库,注册仓库(注册仓库:组件实例身上多一个属性$store属性)
写法一:
// state:仓库存储数据的地方
const state = {};
// mutations:修改state的唯一手段
const mutations = {};
// actions:处理action,可以书写自己的业务逻辑,也可以处理异步
const actions = {};
// getters:计算属性,简化仓库数据,让组件获取仓库的数据更方便
const getters = {};
export default new Vuex.Store({
state,
mutations,
actions,
getters,
})
写法二:
Vuex.Store({
//传配置对象
state:{},
mutations:{},
actions:{},
getters:{},
});
模块式开发
- 创建模块小仓库index,大仓库里引入小仓库 引入Vue Vuex,使用插件 use const state、actions、mutations 创建并暴露store
(1)在state里初始化数据【注意不能随便初始化,根据接口返回值进行初始化,返回值是数组就初始化数组...】
(2)配置actions【内含判断等函数决定是否执行操作,返回给mutations对应的执行操作和要执行的数值(加还是减,谁加谁减)】commit xxx(context,value){}
调用封装的api文件(记得引入,通过API里接口函数调用,向服务器发请求,获取服务器数据)
(3)配置mutations【对应要操作的值进行对应操作(加减)】XXX(state,xxx){把对应state内的值改为传来的xxx}
(4)操作完返回改变state的数据xxx:[]
【组件读取vuex中数据:$store.state.sum
】,
【组件修改vuex数据:store.commit`('mutations中的方法名',数据)】 备注:若没有网络请求或其他业务逻辑,组件中也可以越过actions,即不写dispatch,直接编写commit
- main.js创建vm时传入store
- 组件里运用引入{mapState} 'vuex'
计算属性里
...mapState({ xxx:(state)=>{} })
async异步函数:包含0-多个await表达式 await 表达式会暂停整个 async 函数的执行进程并出让其控制权,只有当其等待的基于 promise 的异步操作被兑现或被拒绝之后才会恢复进程。promise 的解决值会被当作该 await 表达式的返回值。
mapState方法:用于映射state中的数据
mapGetters方法:用于映射getters中的数据
mapMutations方法:用于帮助我们生成与mutations对话的方法,即:包含$store.commit(xxx)
的函数
商品分类三级联动展示动态数据业务TypeNav
全局组件放components中
-
组件挂载完毕,向服务器发送请求 TypeNav/index: mounted通知Vuex发送请求,获取数据,存储于仓库中
this.$store.dispatch('categoryList')
【注】mounted:在模板渲染成html后调用,通常是初始化页面完成后,再对html的dom节点进行一些需要的操作。 -
store/home/index.js: (1)写函数:通过API里的接口函数调用,向服务器发请求,获取服务器数据 (2)引入当初写的API reqCategoryList,返回一个promise对象 (3)actions异步接收,注意这里收到的参数是context,context是一个对象,我们需要里面的commit,所以这里直接把commit解构出来{commit} (4)若code==200,提交数据给mutations,result.data (5)mutations:把传来的值代入state.categoryList = categoryList;
-
组件里运用,引入{mapState} 'vuex' 计算属性中:
...mapState({ categoryList:(state)=>{返回对应的categoryList}
简化:categoryList:state=> {}
遍历数组时,用slice(0,16)限制一下数组长度 categoryList: (state) => state.home.categoryList.slice(0,16)
完成一级分类的背景效果
第一种解决方案:采用样式完成 CSS hover 第二种解决方案:
-
组件身上data里返回一个对象(currentIndex:-1)用响应式数据去存储鼠标移动上的索引值
@mouseenter="changeIndex(index)"
-
methods:{}鼠标进入修改响应式数据changeIndex(index)属性{this.xxx=index}
-
组件存一个:class="{cur:currentIndex==index}"当符合时加上这个类
-
加上.cur这个类的样式
-
鼠标离开的索引值@mouseleave="leaveIndex"【注意判断该给谁】
当有多个列表元素需要绑定事件时,一个一个去绑定即浪费时间,又不利于性能,这时候就可以用到事件委托,给他们的一个共同父级元素添加一个事件函数去处理他们所有的事件情况。
完成动态展示2|3联动结构
方式一:.item-list的display:none; &:hover {.item-list 的display:block}
方式二: :style="{display:currentIndex==index?'block':'none'}"
防抖与节流(优化)
卡顿现象: 正常情况(用户慢慢的操作) :鼠标进入,每一个一级分类h3,都会触发鼠标进入事件 非正常情况(用户操作很快) :本身全部的一级分类都应该触发鼠标进入事件,但是经过测试,只有部分h3触发了。就是由于用户行为过快,导致浏览器反应不过来。如果当前回调函数中有一些大量业务,有可能出现卡顿现象。
- lodash插件:封装了函数防抖与节流业务[闭包+延迟器]
- 防抖:前面的所有的触发都被取消,最后一次执行在规定的时间之后才会触发,也就是说如果连续快速的触发,只会执行最后一次
let input = document.querySelector('input');
//文本发生变化立即执行
input.oninput = _.debounce(function(){
console.log('ajax发送请求')
},1000)
//用户输入停止后一秒才执行一次
- 节流:在规定的间隔时间范围内不会重复触发回调,只有大于这个时间间隔才会触发回调,把频繁触发变为少量触发
_.throttle
//throttle回调函数别用箭头函数,可能出现上下文this - 区别:防抖只执行最后一次,节流规定时间内只执行一次
三级联动节流
按需加载,函数防抖与节流
- 引入lodash全部功能函数:
import xxx from 'lodash'
;按需加载比较好【注意是什么暴露,默认暴露不用加花括号】import throttle from 'lodash';
changeIndex:xx.throttle(function(index){ },50),
问题一:点击a标签的时候,才会进行路由跳转【怎么能确定点击的一定是a标签】 问题二:如何区分是 一级、二级、三级分类的标签 。
- 给总list绑定点击跳转事件@click="goSearch"
- 把子节点当中所有a标签加上自定义属性
data-categoryName
再goSearch(){}
- 获取到当前出发这个事件的节点[h3、 a、dt、d1],看有无data-categoryname
let element = event. target ;//触发事件的对象 (DOM元素) 的引用,用来实现事件委托
- 节点有一个属性dataset属性, 可以获取节点的自定义属性与属性值
let {categoryname,category1id,category2id,category3id} = element.dataset;
- 如果标签身上拥有categoryname:则一定是a标签 同理加上自定义属性:data-category1Id="c1.categoryId"判断为几级分类
if(categoryname){
//跳转的路由
let location = {name:'search'};
let query = {categoryName:categoryname}
if(category1id){
query.category1Id = category1id;
}else if(category2id){
query.category2Id = category2id;
}else if(category3id){
query.category3Id = category3id;
}
//整理传递的参数
location.query = query;
//路由跳转
this.$router.push(location);
}
}
三级联动路由跳转与传递参数:
this.$router.push(name:"search",query:{categoryName:'xxx', 2id:'xx'})
商品分类显示与隐藏
- 添加v-show,值为show
- 初始化show为true
- 当组件挂载完毕,判断TypeNav在哪个路由下,让show属性变为false 如果不是Home路由组件,将typeNav进 行隐藏
- 当鼠标移入的时候,让商品分类列表进行展示
enterShow()
- 判断TypeNav在search下,当鼠标离开的时候,让商品分类列表进行隐藏
leaveShow()
Vue封装的过度与动画:
- 在插入、更新或移除 DOM元素时,在合适的时候给元素添加样式类名
- 使用包裹要过度的元素,并配置name属性【有name属性就得以xxx-enter等来配置属性】
- v-enter:进入的起点,v-enter-active:进入过程中,v-enter-to:进入的终点,v-leave:离开的起点,v-leave-active:离开过程中,v-leave-to:离开的终点
注意:在Vue当中,可以给 (某一个节点)|(某一个组件)添加过渡动画效果。但是需要注意,节点|组件务必出现v-if|v-show指令才可以使用。
TypeNav三级联动性能优化
问题:home切换到search或者search切换到home,会发现一件事情,组件在频繁的向服务器发请求。项目中如果频繁的向服务器发请求,很耗性能,因此需要进行优化。
- 问题:为什么会频繁的向服务器发请求获取三级联动的数据? 原因:因为路由跳转的时候,组件会进行销毁的【home组件的created:在向vuex派发action,因此频繁的获取三级联动的数据】 解决:只需要发一次请求,获取到三级联动的数据即可(this.$store.dispatch("categoryList"); ),不需要多次。 最终解决方案:在App.vue(app.vue中mounted只运行一次)
合并参数
- 三级联动导航TypeNav进入搜索页面,有query参数 判断:路由跳转时有params参数,捎带传递过去 (没有params参数也返回一个空对象,也能进入)
if (this.$route.params) {
location.params = this.$route.params
//动态给location配置对象添加query
location.query = query;
//路由跳转
this.$router.push(location);
}
- header头部进入搜索页面,有params参数 判断:路由跳转时有query参数,捎带传递过去
if(this.$route.query){
let location ={name:"search",params:{ keyword:this.keyword || undefined}};
location.query = this.$route.query;
this.$router.push(location);
}
mock数据
官方地址:mockjs.com/ 第一步:安装依赖包mockjs: 安装:npm install mockjs 第二步:在src文件夹下创建一个文件夹,文件夹mock文件夹。 第三步:准备模拟的数据 把mock数据需要的图片放置于 public 文件夹中! 比如:listContainer中的轮播图的数据
[
{id:1,imgUrl:'xxxxxxxxx'},
{id:2,imgUrl:'xxxxxxxxx'},
{id:3,imgUrl:'xxxxxxxxx'},
]
第四步:在mock文件夹中创建一个mokeSever.js文件,通过mokejs插件实现模拟数据 引入Mock,moke文件夹里面的文件
模拟数据JSON:没有空格,最好使用格式化插件进行格式化。 注意:webpack默认对外暴露模块:图片、json
第五步:通过Mock.mock方法进行模拟数据
//mock数据:第一个参数请求地址:第个参数: 请求数据
Mock . mock( "/mock/banner",{code: 200, data: banner});//模拟首页大的轮播图的数据
Mock . mock(" /mock/floor" , {code: 200 , data :f1oor});
第六步:回到入口文件,引入mockServe.js mock需要的数据|相关mock代码页书写完毕,mock当中serve.js需要执行一次
import ' @/ mock /mockServe';
(不用暴露,因为在其他文件用不到,只要在其他文件引入执行以下就行了)
第七步:Banner轮播图
- 创建mockRequest:改接口地址/mock,【axios实例:baseURL:'/mock'】
- 引入mockRequest
- index里mockRequest专门获取模拟数据用的axios实例(banner轮播图接口)reqGetBannerList
- 找ListContainer对应的index,组件加载完通过vuex发ajax请求,存储在仓库dispatch anctions加函数getBannerList(打印看看是否正确)mutations加函数GETBANNERLIST,state加初始化bannerList
- 组件用mapState拿数据,计算属性将数据传给组件state.home.bannerList
在开发项目的时候:单元测试,某一个功能完毕,一定要测试是否OK
swiper基本的使用
1.swiper移动端可以使用,pc端也可以使用。
2:swiper常用的场景即为轮播图----【carousel:轮播图】
官网地址:www.swiper.com.cn/
3.Swiper使用步骤:
安装:npm i --save swiper@7
第一步:引入依赖包【swiper.js|swiper.css】
第二步:静态页面中结构必须完整【container、wrap、slider】,类名不能瞎写
第三步:初始化swiper实例
swiper在Vue项目中使用
(开发ListContainer组件【首页banner图片】)
提示:卸载插件,可以不用删除node_modules文件夹,可以使用npm uninstall xxxx进行卸载
- swiper安装到项目当中
- 在相应的组件引入swiper.js|swiper.css (引入样式)
注意:home模块很多组件都使用到swiper.css,没必要在每一个组件内部都引入样式一次,只需要在入口文件引入一次即可。
- 结构:在相应组件遍历组件的几个轮播图,carousel是轮播图;对应图片要做响应式,改地址
- 初始化swiper实例在哪里书写? 对于Vue一个组件而言,mounted[组件挂载完毕:相应的结构就有了] mounted-->组件挂载完毕
- 动态效果为什么没有出来? 初始化swiper实例之前,页面中的节点(结构)务必要有,现在结构的数据是服务器返回传过去的,不是一个已经确定的数据 Swiper需要获取到轮播图的节点DOM,才能给swiper轮播添加动态效果,因为没有获取到节点。
- 第一种解决方案,延迟器(不是完美的解决方案)setTimeout(,2000) created:created执行与mounted执行,时间差可能2ms updated:生命周期钩子,用于数据更新。如果组件有很多响应式(data),只要有一个属性值发生变化updated还会再次执行,再次初始化实例。
总结:第一种解决方案可以通过延迟器(异步)去解决问题,但是这种解决方案存在风险(无法确定用户请求到底需要多长时间),因此没办法确定延迟器时间。
Swiper在Vue项目中使用完美解决方案
watch+nextTick
1.watch:监听属性,watch可以检测到属性值的变化,当属性值发生变化的时候,可以出发一次。
Vuex当中的仓库数据bannerList(组件在使用):
handler(newValue,oldValue)
,把swiper放这里面
【组件实例在使用仓库中的bannerList,组件的这个属性bannerList一定是发生过变化,watch可以监听到。】
问题:
//当前这个函数执行:只能保证bannerList数据已经有了,但是你没办法保证v-for已经执行结束了
//v-for执行完毕,才有结构[现在在watch当中没办法保证的]
2.组件实例的一个方法:$nextTick
this.$nextTick(()=>{})
nextTick官网解释: 在下次DOM更新, 循环结束之后,执行延迟回调。在 修改数据之后 立即使用这个方法,获取更新后的DOM。 【注意】组件实例的$nextTick方法,在工作当中经常使用,经常结合第三方插件使用,获取更新后的DOM节点
开发Floor组件
Floor组件:被复用的组件(重复使用两次) 1.Floor组件获取mock数据,发请求的action书写在哪里? 派发action应该是在父组件的组件挂载完毕生命周期函数中书写,因为父组件需要通知Vuex发请求,父组件获取到mock数据,通过v-for遍历 生成多个floor组件,因此达到复用作用。 2.父组件派发action,通知Vuex发请求,Home父组件获取仓库的数据,通过v-for遍历出多个Floor组件 3.v-for|v-show|v-if 这些指令可以在自定义标签(组件)的身上使用 4.为什么在Floor组件的mounted中初始化SWiper实例轮播图可以使用. 因为父组件的mounted发请求获取Floor组件,当父组件的mounted执行的时候。Floor组件结构可能没有完整,但是服务器的数据回来以后Floor组件结构就一定是完成的了,因此v-for在遍历来自于服务器的数据,如果服务器的数据有了,Floor结构一定的完整的。否则看不见Floor组件。
获取search模块数据
- 接口:reqGetSearchInfo获取搜索模块地址 /api/list 请求发送post,带参数,看axios文档写参数{url,method,data}
注意:搜索的接口,需要传递参数,至少是一个空对象(KV没有,但是至少给服务器一个对象)
- 仓库引入三级联动
问题:index.js:20 Uncaught (in promise) TypeError: (0 , _api__WEBPACK_IMPORTED_MOD 解决:引入出错,search引入忘记加中括号:
import { reqGetSearchInfo } from "@/api";
展示商品列表数据
getters:简化仓库中数据 1.goodList(state)返回对应的要简化的数据 getter里的函数参数是指当前这个仓库里的数据,不是大的仓库数据。且没有home、search之分,没有模块化。
有返回undefined的问题,所以要 || []
2.组件里computed里mapGatters引入getter化简的数据 3.改组件 v-for
根据用户的搜索条件展示不同的数据。
根据前台传递参数决定的。根据不同条件,展示不同的数据。----->取决于后台返回的数据 1:发请求,获取搜索模块的数据 2:根据用户搜索的条件携带参数给服务器,展示用户搜索的内筒
问题:mounted组件挂载完只加载一次——>封装成一个函数 解决:getData(){ (,this.xxxx)}向服务器发请求获取search模块数据(据参数不同返回不同数据展示) data里定义为响应式数组xxxx(){xxx:""},mounted调用函数this.getData()
问题:用户条件可以发生多次变化,但是请求只会发一次【mounted中书写的】 解决: 1.组件挂载完毕前执行一次beforeMount【比mounted先】获取路由带来的参数
this.xxxx.category1Id=this.$route.query.category1Id;
合并参数: Object.assign(xxxx,query,params)
,改为
Object.assign(this.xxxx,this.$route.query,this.$route.params)
2.处理SearchSelector模块
(1)引入vuex
(2)计算属性引入列表数据
(3)组件变更v-for
3.监听路由变化$route(newValue,oldValue),再次请求之前整理的服务器参数(同样获取方式)
4.再次发送ajax请求this.getData()
5.每次请求完毕或者请求之前,把相应的1.2.3级分类id置空,好接收下一次的
面包屑的业务完成
- v-if设置组件
- 动态开发面包屑中分类名:
点击x删除分类名字(函数removeCategoryName)时:
1.把带给服务器的参数undefined以及对应的1.2.3级分类id置undefined(其他地方也全改undefined)
2.向服务器发请求//监视到变化就会自动调用,所以这里不用请求
3.地址栏也修改路由跳转到search:如果有params,带
params:this.$route.params
,没有不带this.$router.push({name:""});
严谨!
1.请求的性能优化: 发一个请求,需要向服务器携带参数:带100个参数 带1参数 【消耗宽带】 2.对于给服务器携带的参数:如果数值为undefind,向服务器发请求的时候,参数步携带给服务器的 3.带给服务器参数可有可无时,置空还是会把当前字段带给服务器,但是置为undefined,这个字段不会带给服务器
- 动态开发面包屑中关键字
搜索时,面包屑对应的关键字面包屑
1.点击事件removeKeyword
2.删除关键字:keyword置undefined
3.再发请求
getData()
4.header文本框置空【兄弟组件通信】: main.js里配置vm全局总线beforeCreate(){$bus}
【[this是vm]】 请求完,通知兄弟组件header清除关键字$bus.$emit("clear")
,事件发送者来触发事件 去header中,绑定全局事件总线,事件使用者绑定事件,周期$bus.$on
【销毁事件berforeDestory$bus.off
】"clear",让keyword置空 事件组件挂载完毕后,进行路由跳转if(query) - 动态开发面包屑中品牌信息 1.点击事件tradeMarkHandler(参数信息),给父组件传递信息【子父通信】 2.品牌信息处绑定自定义事件 @trademarkInfo="trademarkInfo" 3.子组件处{}:{}中】 5.再次发请求展示页面 6.品牌展示面包屑trademark.split(":")[1],点击事件removeTradeMark 7.removeTradeMark中undefined品牌信息,再次请求(不需要自己跳自己是因为地址栏没有变化)
- 平台属性的数据进行动态展示
1.平台售卖属性展示处自定义事件@attrInfo,传两个参数attr/attrValue
2.子组件处$emit传递参数
3.父组件整理字符串 props=属性id:属性值:属性名
4.数组去重!
if(props.indexOf(props)==-1){props.push(props)}
5.再次发请求展示页面 6.展示面包屑v-for attrValue,点击事件removeAttr(需传个索引值),再次整理参数,再次请求
兄弟组件通信:全局事件总线 子父通信:自定义事件(点击事件的方法来触发一个自定义事件,将所需要传递的值作为
$emit
的第二个参数,该值将作为实参传给响应自定义事件的方法:父组件 引入子组件–>注册子组件–>引用子组件)${}:${}
:模板字符串,变量需写在${}
中split(" ")
使用一个字符作为分隔符splice(index,howmany,item1,.....,itemX)
:删除或者添加元素
排序业务
- 红底类名显示与否
:class="{active: isOne/isTwo}"
,计算属性配置isOne、isTow - 箭头:谁有类名谁有 iconfont
:class=xxx:isAsc,xxx2:isDesc
- 绑定点击事件:changeOrder(传参flag):代表点的是哪个:originOrder,originFlag,originSort 新newOrder,if flag==originFlag,originFlag:三元判断定asc和desc else newOrder flag:‘desc’
- newOrder赋予searchParams,再次请求
indexOf() 方法可返回某个指定的字符串值在字符串中首次出现的位置,对大小写敏感!如果要检索的字符串值没有出现,则该方法返回 -1
分页器
分页器,components全局组件Pagination,main引入,静态组件放进去,改改加个居中text-align pageNo当前页数,pageSize每一页多少条数据,total总共多少数据(另外获取一共多少页),continues连续的页码数5/7
假数据测试
- 分页器组件处传递假数据测试,再用服务器数据(total,pageSize,pageNo,continues)
- props接收假数据,computed计算出页码数totalPage(Math.ceil()),连续页码起始数结束数startNumAndEndNum,let start、end(注意不正常现象,页数少于continues),parseInt(continues/2) ,有bug(当前第一二页),纠正if小于1,改1同时改end,大于totalPage,改totalPage,同时改start。
- 利用startNumAndEndNum配置button,v-if 上中下三部分button
Math.ceil()取大整数 parseInt() 方法用于将字符串参数作为有符号的十进制整数进行解析
改为真数据
- 分页器组件处修改为动态searchParams(total得从组件中调用出来mapState),自定义事件@getPageNo
total:state => state.search.searchList.total
(出错位置) - 给上一页下一页配置:disable当前第一或最后一页时不能使用
- 各个按钮配置点击事件,利用
$emit
传递参数给自定义事件getPageNo - 父组件拿到事件页数,getPageNo(pageNo)整理参数,调用函数
- 点击谁谁有类名:active,自己配置active的样式
开发详情业务
- 静态页面组件(还没有注册为路由组件):引入路由组件,配置路由组件 1 点击商品时要跳转到详情页,路由跳转时要带上产品ID给详情页(skuid占位) 2 去对应组件把a改router-link,to跳转(别忘了带id参数params) 3 把配置路由信息提取出来,新建routes.js放置,对外暴露,注意用[]括住 4 路由index文件再引入,对外暴露vm 5 滚动行为的设置 文档查询使用滚动条使用方法:router.vuejs.org/zh/guide/ad…
scrollBehavior(to, from, savedPosition) {
// return 期望滚动到哪个的位置
return { x: 0, y: 0 };
}
- API 请求接口 获取产品详情页信息接口 /api/item/{ skuId } 请求方式 get reqGoodsInfo skuid
- vuex 获取产品详情信息 写小仓库detail.js,大仓库中合并,getGoodInfo(,skuid){},GETGOODINFO,goodInfo,dispatch
- 完成动态业务 getters简化数据,返回对象注意可能是undefined,对应组件v-show
- 放大镜、小图列表 Zoom子传父 :a=" b.c"
问题:如果服务器数据没有回来,错误警告undefined 解决方法: 1.在Zoom、Detail子组件的计算属性中写的是 || [{ }],(看是对象{}还是数组[]) 2.在父组件中props用对象写法,default用函数写法返回 [{ }] 小图列表同理 v-for(slide)
- swiper轮播图:
引包,样式,组件,new web实例,watch监听数据skuImageList
new Swiper(容器为refs命名的cur,this.$refs.cur)
多图片显示:slidesPerView: 3,slidesPerGroup: 1,($nextTick)
, 点击加边框: changeCurrentIndex(index),初始化0(两个都要),给图片加,别加错地方 改完框,兄弟通信$emit
(mounted里的change函数this.$bus.$emit
('getIndex',this.currentIndex);) (mounted里this.$bus.$on('getIndex',(index)=>{})
)
computed:{
imgObj(){
return this.skuImageList[this.currentIndex] || {};
}
},
mounted(){
this.$bus.$on('getIndex',(index)=>{
this.currentIndex = index;
})
},
event处,@mousemove= handler handler(){} ref=mask mark=this.$refs
.mask event.offsetX mask.offsetWidth,约束范围,mask.style.left=left.top算px,再将放大的部分lef/top修改
※大图的移动方向和遮罩层相反,又因为大图放大了2倍,所以移动的距离也放大2倍。
ref 加在普通的元素上,用this.$refs.name获取到的是dom元素
项目中控制台 vue-warn:警告(不影响的你程序),对于你的代码提出一个警告。 对于程序没有任何影响,俗称假报错。
- 商品售卖属性 spuSaleAttrList简化数据,choose里组件替换,类看有没有isChecked==1 @click点击事件,排他属性changeActive(传参),注意要把整个列表也传,才能遍历,遍历全部属性值isChecked为零没有高亮 arr.forEach(){},将要高亮的这个组件改为1、
- 购物车: 表单元素修改产品个数 初始化skuNum,v-model=skuNum @click(注意判断是否大于一) @change=changeSkuNum表单元素修改产品个数changeSkuNum(event)用户输入的event.target.value文本*1(为了看是不是非法值),输入非法isNaN(value){ =1}或者让skuNum得到其值的parseInt整数值
添加购物车页面:
/api/cart/addToCart/{ skuId }/{ skuNum } POST
- reqAddOrUpdateShopcart
- addOrUpdateShopcart
服务器写入数据成功, 并没有返回其他的数据,只是返回code=200, 代表这次操作成功 因为服务器没有返回其余数据,因此咱们不需要三连环存储数据 一个函数加上scync,返回值一定是一个promise对象【成功/失败】 标记成功与失败 Promise.reject(new Error("")) - addShopcar: 1:发请求---将产品加入到数据库(通知服务器)dispatch //知道是否成功利用async await获取返回值 2:服务器存储成功-----进行路由跳转传递参数 try catch(error) 3:失败,给用户进行提示 alert 4.成功,路由跳转 引入,注册路由 AddCartSuccess,跳转this.$router.push({name:' '}),注意传参数(会话存储+query)。 会话存储:在路由跳转的时候还需要将产品的信息带给下一级的路由组(转字符串) sessionStorage.setItem/getItem JSON.stringify() 5.对应组件获取数据(计算属性转回对象)JSON.parse 6.看商品详情:a标签改router-link并传递参数(把id带过去) 7.购物车结算:跳转购物车页面
try catch 产品信息的数据[比较复杂:skuInfo] , 通过会话存储(不持久化,会话结束数据在消失) sessionStorage.setItem/getItem
问题:Cannot read properties of undefined (reading 'forEach') 解决:把forEach对应的函数放在计算属性里,不要放methods
全选框勾选及全部产品的勾选状态:
全选框存在底层问题,点击全选时,分项checked变动,却不会发请求 updataAllCartChecked,获取当前选中状态,派发action(updateAllCartIsChecked),promise.all
游客身份获取购物车数据
uuid网站:www.npmjs.com/package/uui…
- 引入v4,"uuid"。建立store/util/uuid_token(封装游客临时身份的模块,生成一个随机字符串,不能再变了)
- 暴露getUUID(export const getUUID=函数),要生成一个随机字符串,且每次执行不能发生事化,游客身份持久存储: 先从本地存储获取uuid (看一下本地存储里面是否有localStorage.getItem UUIDTOKEN),如果没有,生成游客临时身份=uuidv4(),本地存储存储一次localStorage.setItem,返回uuid值
- detail.js引入getUUID,goodInfo里配置游客临时身份
- 把uuid传给路由: 去请求拦截器,引入store仓库,看看仓库是否接收到,接收到就给请求头添加一个字段config.headers.userTempId=xxx(和后台确定的userTempId)
config是什么?配置对象,可以让进度条开始动 在请求头添加一个字段(和后台确定的userTempId)
注册业务
- 引入组件,将需要的图片找到对应文件放入
- assets文件夹---放置全部组件共用静态资源
- 在样式当中也可以使用@符号[src别名],切记在前面加上~ (表单验证先不处理)
- data收集表单数据---手机号
- user.js(登录注册模块store),引入配置
- 获取验证码的接口/api/user/passport/sendCode/ {phone} get
- reqGetCode,获取验证码getCode,(获取验证码的这个接口: 把验证码返回,但是正常情况,后台把验证码发到用户手机上),简单判断一下验证码&&,将组件的code属性值变为仓库中验证码[验证码直接自己填写了],password1,password2表单数据,用户注册
- 注册业务 url:/api/user/passport/register , method:post , phone code password reqUserRegister,userRegister
1.const { phone } = this; //this是一个对象,包含了phone属性,左边解构出来给phone 2.复选框是通过checked属性判断是否勾选的 3.为什么还要返回promise? 要用验证码返回的数据 到时候可以看它是否在输入框中显示不就可以知道是成功还是失败了 4.else Promise.reject(new Error('faile'));不能return 5.问题:user.js:35 Uncaught (in promise) Error: faile 解决:一种参考原因,接口请求数据已经正常返回,识别时判断是
result.code!== 200
就算请求失败,从而Promise.reject(rejection)
报错,而我返回的正确请求返回值result.code ===200
,,所以产生了上面的报错,修改为result.code判断是否正常就解决了
登录业务
(先注册才能登录)----存储用户信息(名字,密码) 登录成功的时候,后台为了区分你这个用户是谁-服务器下发token [令牌:唯一标识符] 登录接口:做的不完美,一般登录成功服务器会下发token,前台持久化存储token,[带着token找服务器要用户信息进行展示]
api,URL : /api/user/passport/login,method : post,phone password
reqUserLogin, userLogin,判断正常,把token传给mutations(state记得初始化token),登录成功,路由跳转home首页
阻止默认行为登录按钮跳转页面,from 表单的action=“##”删除,click改click.prevent【form表单里面有很多古老的问题。里面的btn 默认就是submit类型 还有input回车有时候也会跳转路径】 注意:vuex仓库存储数据----不是持久化
获取用户信息(token)
/api/user/passport/auth/getUserInfo get
- 请求reqUserInfo 三连环getuserInfo userInfo
- home组件mounted中 获取用户信息首页展示dispatch getUserInfo userTempld
- request里请求拦截器,携带请求头token给服务器,config.headers.token=store.state.user.token;
- 用户登录成功获取到token的时候,持久化存储token(初始化也得改) localStorage.setItem("TOKEN",result.data.token) localStorage.getItem("TOKEN") 没有的时候是null和‘’没区别 一级动态展示header组件内容,显示组件v-if v-else 用户名信息计算属性 userName{this.$store.state.user.userInfo.name}
- 刷新就退出登录------>token消失
退出登录
logout点击事件 写api,reqLogout /api/user/passport/logout GET 写action,userLogout【actions里不能修改state,得给mutations】 写mutations,CLEAR,清除state仓库里token、userInfo、localstorage 派发action,dispatch 退出成功,回到首页
交易业务Trade
-
静态组件复制到page,引入配置路由Trade
-
购物车结算绑定点击事件(或者用router-link)
-
api获取用户地址信息reqAddressInfo get /api/user/userAddress/auth/findUserAddressList 仓库开trade.js ,引入配置总仓库,引入req actions获取用户地址信息 getUserAddress mounted组件挂载完毕,派发action mountain修改state GETUSERADDRESS 修改address 初始化address数组
-
api获取用户购物车清单信息reqOrderInfo get /api/order/auth/trade 引入req actions获取清单信息 getOrderInfo mounted组件挂载完毕,派发action mountain修改state GETORDERINFO 修改orderInfo 初始化orderInfo对象
-
组件mapState,计算属性拿数组对象里的元素addressInfo v-for,改组件内容,注意改class使用状态,点击事件选择地址changeDefault(address现在,addressInfo全部),methods配置点击事件,isDefault 遍历为0,点击的对象为1 配置提交订单时最终地址:计算属性userDefault,find找isDefault==1的,修改对应组件
提交订单业务
- api提交订单接口reqSubmitOrder /api/order/auth/submitOrder?tradeNo={tradeNo} post
- 组件里,引入req(引入全局以后不需要引入了),把router-link改为a,绑定点击事件submitOrder
- 配置函数:获得交易编码tradeNo,获得data= {}在接口文档复制,修改值
- 初始化一个订单号orderId(data),派发this.$API.reqSubmitOrder 同时传送参数,返回值的data就是orderId
- 如果提交订单成功,路由跳转,传送orderId,/pay?orderId=xxx
格式:
this.$router.push({path:
/search/${this.keyword}, query:{k: this.keyword.toUpperCase()}})
- 不成功弹出来错误
- 编程式路由传递参数:
// 第一种:字符串形式
this.$router.push("/search/" + this.keyword + "?k=" + this.keyword.toUpperCase());
// 第二种:模板字符串
this.$router.push(`/search/${this.keyword}?k=${this.keyword.toUpperCase()}`)
// 第三种:参数对象形式
this.$router.push({name: "search", params:{keyword: this.keyword}, query:{k: this.keyword.toUpperCase()}})
this.$router.push({path: `/search/${this.keyword}`, query:{k: this.keyword.toUpperCase()}})
支付页面
- 引入配置路由
- 计算属性获得路由的query参数orderId
- api获取支付信息reqPayInfo /api/payment/weixin/createNative/{orderId} get
- 组件里组件挂载完毕后,用函数获得reqPayInfo getPayInfo 【组件中生命周期函数不能加async!】
- 配置函数getPayInfo {xx.xx.reqPayInfo()},如果成功,组件存储支付信息
- data配置payInfo对象
- 组件修改内容
- 立即支付组件也改为a标签 点击事件open
- open函数配置ui组件弹出框(复制黏贴),修改文本,照着文档里的介绍设置样式
微信支付业务
- 二维码生成:qrcode 安装
npm i qrcode --save
照文档教程写 - 引入,传字符串生成url=(要await)
展示
<img src=${url} />
【注意${url} /之间有个空格,不然展示不了图片】 - 知道支付是否成功,成功路由跳转,失败则提示
- 接口查询支付是否成功 初始化timer:null if判断this.timer,未成功开新定时器 this.timer=setInterval(()=>{}) 获取支付状态 reqPayStatus 成功,清除定时器clearInterval(this.timer);timer=null,保存支付成功返回的code,关闭弹出框$msgbox.close(),跳转下一页
获取订单列表
获取个人中心数据
- reqMyOrderList ,/api/order/auth/{page}/{limit}, get, page ,limit
- mounted 获取订单数据 this.getData(),获取当前page limit,配置函数获取数据,初始化数据page1 limit3
- 派发dispatch,修改组件
- 用之前写的分页器,修改配置,写getPageNo当前页面函数
未登录的导航守卫:不能去交易相关,支付相关,个人中心 to.path判断去哪个,把未登录想去的存储于地址栏中 .indexOf('')!=-1 { next('/ ?redirect='+toPath)}//添加query参数 登录页面的判定登录成功后,看是否有query跳转到对应页面 .query.redirect toPath
路由独享守卫
- 路由独享守卫:专门负责某一个路由 只有从购物车界面才能跳转到交易页面(创建订单) 只有从交易页面(创建订单)页面才能跳转到支付页面 只有从支付页面才能跳转到支付成功页面
- 在routes.js里,对应路由下配置beforeEnter
- 导航守卫:全局守卫->项目当中有任何路由变化【a->b,b->d】触发。
- 用户登陆了:去交易页面:从购物车才能跳转到交易页面。 next():你本来想去哪里,我就放行,你就去完事了。 next('/login'):执行守卫放行到执行的路由。 next(false):路由跳转的时候,从哪里来回那里去。
element-ui
第一步:项目中安装element-ui组件库 [2.15.6版本:Vue2]
npm i --save element-ui
第二步:在入口文件引入elementUI组件库
- 全部引入【不采用:因为项目中只是用到一个组件,没必要全都引入进来】
- 按需引入【按照开发需求引入相应的组件,并非全部组件引入】
1.安装babel-plugin-component:
npm install babel-plugin-component -D
2.修改 .babelrc,即为babel.config.js文件,项目重启 3.按照需求引入相应的组件
main.js
import { Button } from 'elemetn-ui'
Vue.component(Button.name,Button)
4.组件里使用 <el-button>
Vue.component();
Vue.prototype.$xxx = xxx;
1.问题:Error: Cannot find module 'babel-preset-es2015' 解决:安装babel-preset-es2015 2.问题:Error: Plugin/Preset files are not allowed to export objects, only functions. In D:\moting\01Qian\vue练习\project-SHP\app\node_modules\babel-preset-es2015\lib\index.js 原因:脚手架版本较新,与babel依赖包不兼容 解决:在babel的配置文件中,将预设包中的"es2015"修改为"@babel/preset-env"
个人中心路由搭建center
- 当年学习路由的时候:一级路由、二级路由、三级路由 【二级路由搭建】
- 完成个人中心数据的展示【分页】 复制组件,引入注册路由 /Center/groupOrder/index.vue(团购订单),/Center/myOrder/index.vue(我的订单)文件夹 /Center/index.vue 引入配置组件 route里引入注册二级路由:注册时,要在一级里写children:[{path: component:},{ }] /Center/index.vue 路由组件出口位置配置 router-view a标签改router-link 重定向 在center路由配置下,子组件里加一个path /center对应的redirect /center/myorder 也可以直接一级路由里配置redirect:/center/myorder
路由懒加载
当打包构建应用时,JavaScript 包会变得非常大,影响页面加载。 如果能把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应组件,这样就更加高效了。 component:() =>import("@/view/Home") 修改成这个写法 简写手法
图片懒加载
自定义插件,指令 www.npmjs.com/package/vue… vue-lazyload:图片懒加载
打包上限
打包npm run build
项目打包后,代码都是经过压缩加密的,如果运行时报错,输出的错误信息无法准确得知是哪里的代码报错。
有了map就可以像未加密的代码样,准确的输出是哪一行哪一列有错。
所以该文件如果项目不需要是可以去除掉
vue. config.js配置
productionSourceMap:false
nginx 反向代理
- 为什么访问服务器IP地址就可以访问到咱们项目? ---配置一些东西 http://82.156.11.187/ 刚刚在服务器上=>/root/jch/www/ shangpinhui/dist
- 项目的数据来自于http://39.98.123.211 nginx配置: 1:xshe11进入根目录/etc 2:进入etc目录,这个目录下有一个nginx目录,进入到这个目录[已经安装过nginx:如果没有安装过,四五个文件] 3:如果想安装nginx:yum install nginx 4:安装完nginx服务器以后,你会发现在nginx目录下,多了一个nginx. conf文件,在这个文件中进行配置 5: vim nginx. conf进行编辑,主要添加如下两项 解决第一个问题: location / { root /root/ jch/www/ shangpinhui/dist; index index . html; try_ files uri/ /index. html; } //解决第二个问题 location /api { proxy_ pass http://39 .98.123.211; } 6:nginx服务器跑起来 service nginx start