首先新起一个项目
解决适配问题
引入 lib-flexible 和 px2rem-loader, 打开 build 文件夹, 编辑 utils.JS
- exports.CSSLoaders = function (options) {
- options = options || {}
- var cssLoader = {
- loader: 'CSS-loader',
- options: {
- minimize: process.env.NODE_ENV === 'production',
- sourceMap: options.sourceMap
- }
- }
- const px2remLoader = {
- loader: 'px2rem-loader',
- options: {
- remUnit: 37.5 //1rem = 多少像素 这里的设计稿是 750px.
- }
- }
- ...
- }
main.JS 引入
import 'lib-flexible'
项目里使用设计稿标注的 px, 编译或者打包后会自动转化为 rem
image
使用 Less
cnpm i Less Less-save -S
再编辑 webpack.base.conf 配置 Less-loader
- module: {
- rules: [
- {
- test: /\.Less$/,
- loader: 'style-loader!CSS-loader!Less-loader',
- },
- ...
- ]
- }
解决移动端点击 300ms 延迟
cnpm i fastclick -S
main.JS
- import FastClick from 'fastclick'
- FastClick.attach(document.body)
顶部进度条
cnpm i nprogress -S
main.JS
- import NProgress from 'nprogress' // 引入自定义 CSS 是为了覆盖掉默认的进度条的颜色
- import './assets/CSS/nprogress.CSS'
- NProgress.configure({
- easing: 'ease', // 动画方式
- speed: 500, // 递增进度条的速度
- showSpinner: false, // 是否显示加载 ico
- trickleSpeed: 200, // 自动递增间隔
- minimum: 0.3 // 初始化时的最小百分比
- })
- router.beforeEach((to, from , next) => {
- // 每次切换页面时, 调用进度条
- NProgress.start();
- next()
- });
- router.afterEach(() => {
- // 在即将进入新的页面组件前, 关闭掉进度条
- NProgress.done()
- })
nprogress.CSS
- #nprogress {
- pointer-events: none;
- }
- #nprogress .bar {
- background: #FE571B;
- position: fixed;
- z-index: 1031;
- top: 0;
- left: 0;
- width: 100%;
- height: 2px;
- }
- /* Fancy blur effect */
- #nprogress .peg {
- display: block;
- position: absolute;
- right: 0px;
- width: 100px;
- height: 100%;
- box-shadow: 0 0 10px #FE571B, 0 0 5px #FE571B;
- opacity: 1.0;
- -webkit-transform: rotate(3deg) translate(0px, -4px);
- -ms-transform: rotate(3deg) translate(0px, -4px);
- transform: rotate(3deg) translate(0px, -4px);
- }
- /* Remove these to get rid of the spinner */
- #nprogress .spinner {
- display: block;
- position: fixed;
- z-index: 1031;
- top: 15px;
- right: 15px;
- }
- #nprogress .spinner-icon {
- width: 18px;
- height: 18px;
- box-sizing: border-box;
- border: solid 2px transparent;
- border-top-color: #FE571B;
- border-left-color: #FE571B;
- border-radius: 50%;
- -webkit-animation: nprogress-spinner 400ms linear infinite;
- animation: nprogress-spinner 400ms linear infinite;
- }
- .nprogress-custom-parent {
- overflow: hidden;
- position: relative;
- }
- .nprogress-custom-parent #nprogress .spinner,
- .nprogress-custom-parent #nprogress .bar {
- position: absolute;
- }
- @-webkit-keyframes nprogress-spinner {
- 0% { -webkit-transform: rotate(0deg); }
- 100% { -webkit-transform: rotate(360deg); }
- }
- @keyframes nprogress-spinner {
- 0% { transform: rotate(0deg); }
- 100% { transform: rotate(360deg); }
- }
image
封装 axios 请求
cnpm i axios -S
src 文件夹下新建 http 文件夹, 并在文件夹内新建 API.JS
API.JS
- /* eslint-disable */
- import axios from 'axios'
- import router from 'vue-router'
- import Cookies from 'JS-cookie'
- /**
- * 定义请求常量
- * TIME_OUT,ERR_OK
- */
- export const TIME_OUT = 5000; // 请求超时时间
- export const ERR_OK = true; // 请求成功返回状态, 字段和后台统一
- // export const baseUrl = process.env.BASE_URL // 引入全局 url, 定义在全局变量 process.env 中, 开发环境为了方便转发, 值为空字符串
- // 环境的切换
- console.log('process.env.NODE_ENV:'+process.env.NODE_ENV);
- if (process.env.NODE_ENV == 'development') {
- axios.defaults.baseURL = 'http://API-campus-stg1.pingan.com:8282';
- } else if (process.env.NODE_ENV == 'production') {
- axios.defaults.baseURL = 'http://API-campus.pingan.com';
- }
- // 请求超时时间
- axios.defaults.timeout = TIME_OUT
- // 封装请求拦截
- axios.interceptors.request.use(
- config => {
- config.headers['Content-Type'] = 'application/JSON;charset=UTF-8';
- config.headers['accessToken'] = '';
- if(Cookies.getJSON('loginMsg')){
- config.headers['accessToken'] = Cookies.getJSON('loginMsg').token
- }
- return config
- },
- error => {
- return Promise.reject(error)
- }
- )
- // 封装响应拦截, 判断 token 是否过期
- axios.interceptors.response.use(
- response => {
- let {data} = response
- if (data.responseCode === 202) { // 如果后台返回的错误标识为 token 过期, 则重新登录
- // token 过期移除 token
- localStorage.removeItem('token')
- // 进行重新登录操作
- } else {
- return Promise.resolve(data)
- }
- },
- error => {
- return Promise.reject(error.response)
- if (error.response.status) {
- switch (error.response.status) {
- // 401: 未登录
- // 未登录则跳转登录页面, 并携带当前页面的路径
- // 在登录成功后返回当前页面, 这一步需要在登录页操作.
- case 401:
- router.replace({
- path: '/login',
- query: {
- redirect: router.currentRoute.fullPath
- }
- });
- break;
- // 403 token 过期
- // 登录过期对用户进行提示
- // 清除本地 token 和清空 vuex 中 token 对象
- // 跳转登录页面
- case 403:
- // var toast = Toast.$create({
- // txt: '登录过期, 请重新登录',
- // mask: true
- // })
- // toast.show()
- // 清除 token
- localStorage.removeItem('token');
- store.commit('loginSuccess', null);
- // 跳转登录页面, 并将要浏览的页面 fullPath 传过去, 登录成功后跳转需要访问的页面
- setTimeout(() => {
- router.replace({
- path: '/login',
- query: {
- redirect: router.currentRoute.fullPath
- }
- });
- }, 1000);
- break;
- // 404 请求不存在
- case 404:
- // var toast = Toast.$create({
- // txt: '网络请求不存在',
- // mask: true
- // })
- // toast.show()
- break;
- // 其他错误, 直接抛出错误提示
- default:
- // var toast = Toast.$create({
- // txt: error.response.data.message,
- // mask: true
- // })
- // toast.show()
- }
- }
- return Promise.reject(error.response)
- }
- )
- export default axios
使用方法:
main.JS
- import axios from './http/API'
- Vue.prototype.$http = axios
然后就可以在项目中以 this.$http 来进行请求
路由懒加载(增加首屏加载速度)
- routes: [
- {
- path: '/',
- name: 'index',
- component:resolve=>require(['@/page/index'],resolve)
- },
- ...
- ]
404 页面
- routes: [
- {
- path: '*',
- name: '404',
- meta: {
- title: '404',
- auth:false,// 需要登录
- },
- component:resolve=>require(['@/page/error'],resolve)
- }
- ]
路由鉴权
(1)router/index.JS
给每个路由新增一个 auth 字段来判断是否需要登录
- routes: [
- {
- path: '/',
- name: 'index',
- meta: {
- title: '首页',
- auth:true,// 需要登录
- },
- component:resolve=>require(['@/page/index'],resolve)
- },
- ]
main.JS
- router.beforeEach((to, from , next) => {
- /* 路由发生变化修改页面 title */
- if (to.meta.title) {
- document.title = to.meta.title
- }
- // 每次切换页面时, 调用进度条
- NProgress.start();
- // 对路由进行验证
- if(to.matched.some( m => m.meta.auth)){
- if(!store.state.user.isLogin) { // 未登陆
- next({path:'/login'})
- NProgress.done()
- }else{
- next()
- }
- }else{
- next()
- }
- });
- router.afterEach(() => {
- // 在即将进入新的页面组件前, 关闭掉进度条
- NProgress.done()
- })
其中 store.state.user.isLogin 是 vuex 里来判断用户登录没, 具体实现方式是登录后保存 token 等用户信息到 cookie 里 (JS-cookie 插件), 并设置过期时间为 1 天(以实际项目为准) 然后后面每次请求会带上 token, 如果后台返回 token 过期的 code,commit 并设置 isLogin 为 false, 回到登录页.(不同项目判断有所不同, 具体以项目为准)
promise 兼容 IE
cnpm i es6-promise -S
main.JS
require("es6-promise").polyfill();
引用 vconsole
移动端项目调式怎么可以少了这个神器.
static 文件夹下新建 vconsole.JS, 再去 Git 上拷贝源码下来, 在 index.html 里引入, vconsole 地址: 点这里 https://GitHub.com/Tencent/vConsole/tree/dev/dist
index.HTML
- <script src="./static/vconsole.JS"></script>
- <script>
- var vConsole = new VConsole();
- </script>
备注: 静态文件放在 static 还是 assets 是有区别的, static 下的文件不会被 webpack 处理, 任何放在 static/ 中文件需要以绝对路径的形式引用, 而 assets 会被打包到你的项目里, 正因为这样, 如果 vconsole 放在 assets 文件下引用, 打包后会找不到路径. 一句话总结: static 放别人家的, assets 放自己写的.
配置开发环境生成环境接口
https://www.jianshu.com/p/1fc65f3f4afa
解决 CSS 移动端字母或者文字大小有时会变化的问题
- HTML{
- -ms-text-size-adjust: 100%;
- -webkit-text-size-adjust: 100%;
- }
谷歌下不支持小于 12px, 当字体小于 12px 时 会变成 12px 这个时候我们设置的 rem 及 = 就没有效果了 设置 text-size-adjust 会解决这个问题 禁用 Webkit 内核浏览器的文字大小调整功能
打包后生成很大的. map 文件的问题
在 config/index.JS 文件中, 设置 productionSourceMap: false, 就可以不生成. map 文件
查看打包后各文件的体积
NPM run build --report
来源: http://www.jianshu.com/p/1566a0f80838