1.下载 vue.js文件(交货时,换成vue.min.js)
2.CDN
3.Vue CLI
快速搭建大型单页应用,提供开箱借用的构建工具配置,现代化的前端开发流程
npm i -g @vue/cli // 全局安装vue-cli,用于反复创建相同结构、包含核心代码的项目
vue create my-project // 新建项目my-project
cd my-project // 切入项目
npm run serve // 运行网页
// Please pick a preset ?
Manually select features // 手选
// Choose Vue version ?
Babel、Router、Vuex
// Use history mode for router ?
n // 去除url的#,需服务器重定向,公司用
// Where do you prefer placing config for Babel, ESLint, etc.?
In package.json
// Save this as a preset for future projects ?
n // 不要预设
if ( a < 4 ) { linter语法要求必须加空格,公司里用
a = 1
}
安装 vuex axios
cd my-project
npm install --save vuex axios // install -> i
// main.js 下引入axios
import axios from 'axios'
// main.js 下配置axios
axios.defaults.baseURL = 'http://localhost:8080';
Vue.prototype.axios = axios;
// 使用 axios
this.axios.get( `/user/v1/userRegister/${uname}&${upwd}` )
.then( result=>{
[this.a, this.b, this.c] = result.data;
this.others = result.data.slice(3);
});
引入第三方插件库,迁移静态资源
public > css + img + js + favicon.ico + index.html
boot 的 min-width样式 和 vue 冲突,#app下一级#main的width:100%无效,自己去覆盖
<!-- Bootstrap -->
<link rel="stylesheet" href="./css/bootstrap.css">
<script src="./js/popper.min.js"></script>
<script src="./js/bootstrap.min.js"></script>
<!-- CssReset -->
<link rel="stylesheet" href="./css/reset.css">
<!-- common.css 粘贴进APP.vue的style -->
<!-- JQuery -->
<script src="./js/jquery.min.js"></script>
<!-- Animate.css -->
<link href="https://cdn.bootcdn.net/ajax/libs/animate.css/4.1.1/animate.compat.css" rel="stylesheet">
<!-- Iconfont -->
<!-- <link rel="stylesheet" href="//at.alicdn.com/t/font_2276304_xb8epmk7e3k.js"> -->
提示
// App.vue 初始化内容
<template>
<div id="app">
<router-view/>
</div>
</template>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
}
.container {
width: 100%;
padding-right: 0;
padding-left: 0;
margin-right: auto;
margin-left: auto;
}
@media (min-width: 1200px) {
.container {
max-width: none;
}
}
#main {
min-width: 1200px
}
a:hover {
text-decoration: none;
}
.dropup {
position: relative;
}
.dropdown-box, .dropdown-menus, .dropdowns {
position: absolute;
z-index: 501;
}
</style>
// 删除Home.vue、About.vue后,移除router里的import,并沉默routes[]内全部内容
// 页面组件唯一父元素
<div id="当前页面名称" class="container">
// <style lang="stylus">
<style scoped> // css属性只在当前组件生效,避免重名冲突
// components下子组件引入main.js为全局组件
/* import components */
import MyHeader from './components/MyHeader.vue'
/* mount components */
Vue.component( 'my-header', MyHeader );
// router-link
<router-link :to=" `/details/${ p1.href.split('=')[1] }` "></router-link>
懒加载
// 编译vue为html js css,被浏览器识别 // 模拟服务端解析,个人学习结构时用
npm run build // 编译完会多出 dist 文件
客户端请求时,所有直接被import的页面,会全部打包进一个巨大的app.js文件,发送,造成卡顿
import A from '../views/A.vue'
import B from '../views/B.vue'
...
import Z from '../views/Z.vue'
懒加载理念:优先下载用户想看到的页面,再在后台悄悄地下载其他页面
懒加载原理:当地址栏变成/details时,才会临时执行匿名函数,
新建独立chunk分段,动态引入vue文件,异步加载
{
path: '/details/:lid',
name: 'details',
props: ture,
component: () => import(
/* webpackChunkName: "details" */ // 这段注释指定了文分段名,不能删
'../views/Details.vue'
)
}
客户端network内,多出独立的details.js文件
客户端html内如下:
<link href="/js/details.js" rel="prefetch"> // prefetch 预加载
缺陷:用户不想看的页面也会被下载,偷跑流量
真正的懒加载:连异步加载都不要有,用户请求哪个页面,就临时加载哪个
实现:直接删掉所有页面的prefetch属性,通过新建vue.congig.js的全局配置文件
// 根目录下新建vue.congig.js
module.exports = {
chainWebpack: config => {
config.plugins.delete( "prefetch" )
}
}
err:数据显示成功还报错
monuted阶段axios请求数据后,服务器还没响应回来,就立刻读取变量,变量还是undefined,就会报错
直接打他里给初始值,注意数据类型要一致
err:详情页点按钮切规格,url有反应,页面无反应
// 当前正在看哪个,哪个按钮激活
<router-link class="btn" :class=" {active: sp.lid==lid} "
v-for="(sp,i) of specs" :key="i"
:to="`/details/${sp.lid}`">
{{sp}}
</router-link>
SPA机制:首次url访问,才会触发create、mount阶段
解决办法:watch()使其重新加载页面
methods:{
loadPage(){
this.axios.get("/details",{params:{lid:this.lid}})
.then(result=>{
this.specs=result.data.specs;
...
})
}
},
watch:{
lid(){
this.loadPage();
}
}
产品详情-小图展示
<ul :style="{width: pics.length*62+'px'}"> // 爹宽 = 子宽 * 爹的length
<li v-for="(img,i) of pics" :key="i"> // 根据图片内容动态生成容器宽
<img :src="img.sm">
</li>
</ul>
产品详情-小图展示-左右按钮
data: { times:0 } // 定义times,判断小图位移次数
<ul style="width: 434px; margin-left:-62px"
:style=" {width:pics.length*62+'px', marginLeft:-62*times+'px'} "
>
按钮加事件:点右边li左移,点左边li右移,并限制无限位移
moveLeft (){ this.times<this.pics.length-4 && this.times++ }
moveRight(){ this.times>0 && this.times-- }
进入详情页,预加载指定产品的中图,大图
data: { times:0, i:0 } // 记录当前正在看第几张
pics:[ {} ] // 异步问题 pics不能为空 强行占位
<img :src="pics[i].md"> // 中图
<div :style="{backgroundImage:`url(${pics[i].lg})`}"></div> // 大图
按钮显示隐藏状态
<img :class="{ disabled:times==0 }"> // 左
<img :class="{ disabled:times >= pics.length-4 }"> // 右
小图hover:切换中图、大图
<li @mouseenter="changei(i)">
changei(i){ this.i=i }
放大镜
// mask
<div id="mask" :class="{'d-none':hide}"
:style=" { top:top+'px', left:left+'px' } "
></div>
// div-lg
<div id="div-lg" :class="{'d-none':hide}"
:style="{
backgroundImage: ` url( ${ pics[i].lg } ) ` ,
backgroundPosition: ` ${ -left*16/7 }px ${ -top*16/7 }px `
}"
></div>
// super-mask
<div id="super-mask" @mouseenter="toggle" @mouseleave="toggle" @mousemove="move"></div>
toggle(){
this.hide=!this.hide
}
move(e){ // top:0 left:0
var top=e.offsetY-88; // -88是为了鼠标在黄色mask中间
var left=e.offsetX-88;
if(top<0) top=0; else if(top>176) top=176;
if(left<0) left=0; else if(left>176) left=176;
this.top=top; this.left=left;
}