资讯专栏INFORMATION COLUMN

基于vue-cli3.0构建功能完善的移动端架子

Loong_T / 846人阅读

摘要:对应每一个环境可能都会有所差异,比如说服务器地址接口地址地址等等。具体的值取决于应用运行的模式。会和中的选项相符,即你的应用会部署到的基础路径。

基于vue-cli3.0构建功能完善的移动端架子,主要功能包括

webpack 打包扩展

css:sass支持、normalize.css、_mixin.scss、_variables.scss

vw、rem布局

跨域设置

eslint设置

cdn引入

路由设计、登录拦截

axios、api 设计

vuex状态管理

项目地址: vue-cli3-H5

demo地址: https://zhouyupeng.github.io/vuecli3H5/#/

webpack 打包扩展

vue-cli3.*后目录结构大改,去除了以往的build,config文件夹,要实现配置的改动在根目录下增加vue.config.js进行配置

css:sass支持、normalize.css、_mixin.scss、_variables.scss

使用的css预处理器是sass,对于css mixin,变量这里做了全局引入,并且引入normalize.css 使HTML元素样式在跨浏览器上表现得的高度一致性
vue.config.js配置

css: {
        // 是否使用css分离插件 ExtractTextPlugin
        extract:isProduction ? true:false,
        // 开启 CSS source maps?
        sourceMap: false,
        // css预设器配置项
        // 启用 CSS modules for all css / pre-processor files.
        modules: false,
            sass: {
                data: "@import "style/_mixin.scss";@import "style/_variables.scss";" // 全局引入
            }
        }
    }
vw、rem布局

对于移动端适配方案使用的是网易新闻的方法,
使用vw + rem布局

/**
750px设计稿
    取1rem=100px为参照,那么html元素的宽度就可以设置为width: 7.5rem,于是html的font-size=deviceWidth / 7.5
**/
html {
    font-size: 13.33333vw
}

@media screen and (max-width: 320px) {
    html {
        font-size: 42.667PX;
        font-size: 13.33333vw
    }
}

@media screen and (min-width: 321px) and (max-width:360px) {
    html {
        font-size: 48PX;
        font-size: 13.33333vw
    }
}

@media screen and (min-width: 361px) and (max-width:375px) {
    html {
        font-size: 50PX;
        font-size: 13.33333vw
    }
}

@media screen and (min-width: 376px) and (max-width:393px) {
    html {
        font-size: 52.4PX;
        font-size: 13.33333vw
    }
}

@media screen and (min-width: 394px) and (max-width:412px) {
    html {
        font-size: 54.93PX;
        font-size: 13.33333vw
    }
}

@media screen and (min-width: 413px) and (max-width:414px) {
    html {
        font-size: 55.2PX;
        font-size: 13.33333vw
    }
}

@media screen and (min-width: 415px) and (max-width:480px) {
    html {
        font-size: 64PX;
        font-size: 13.33333vw
    }
}

@media screen and (min-width: 481px) and (max-width:540px) {
    html {
        font-size: 72PX;
        font-size: 13.33333vw
    }
}

@media screen and (min-width: 541px) and (max-width:640px) {
    html {
        font-size: 85.33PX;
        font-size: 13.33333vw
    }
}

@media screen and (min-width: 641px) and (max-width:720px) {
    html {
        font-size: 96PX;
        font-size: 13.33333vw
    }
}

@media screen and (min-width: 721px) and (max-width:768px) {
    html {
        font-size: 102.4PX;
        font-size: 13.33333vw
    }
}

@media screen and (min-width: 769px) {
    html {
        font-size: 102.4PX;
        font-size: 13.33333vw
    }
}

@media screen and (min-width: 769px) {
    html {
        font-size: 102.4PX;

        #app {
            margin: 0 auto
        }
    }


}

vue.config.js配置

loaderOptions: {
    postcss: {
        // 这是rem适配的配置
        plugins: [
            require("postcss-px2rem")({
                remUnit: 100
            })
        ]
    }
}
开发时跨域设置
devServer: {
        open: true, // 启动服务后是否打开浏览器
        host: "127.0.0.1",
        port: 8088, // 服务端口
        https: false,
        hotOnly: false,
        proxy: "https://easy-mock.com/" // 设置代理
    }

配置完后,本地开发环境的axios的baseUrl要写为 "" ,即空字符串。
发布到线上时如果前端代码不是和后台api放在同源下的,后台还需做跨域处理,

eslint standard设置

使用的是JavaScript standard 代码规范,一个好的编码风格它可以帮助减少团队之间的摩擦,代码阅读起来也更加清爽,更加可读性,不要觉得烦,用了都说好。
这是 JavaScript standard 代码规范的全文

自定义配置,在.eslintrc.js里修改,这里是我给出的配置,4个空格缩进,不检查结尾分号,关闭单var 声明,可自行配置

rules: {
    "no-console": process.env.NODE_ENV === "production" ? "error" : "off",
    "no-debugger": process.env.NODE_ENV === "production" ? "error" : "off",
    indent: [
        "error",
        4,
        {
            SwitchCase: 1
        }
    ],
    semi: 0, // 不检查结尾分号,
    // 强制使用单引号
    quotes: ["error", "single"],
    // 关闭函数名与后面括号间必须空格规则
    "space-before-function-paren": 0,
    // 关闭var 声明,每个声明占一行规则。
    "one-var": 0
    }
cdn引入

对于 vue、vue-router、vuex、axios等等这些不经常改动的库、我们让webpack不对他们进行打包,通过cdn引入,可以减少代码的大小、也可以减少服务器的带宽
这里使用的是360的cdn,附上一份公共cdn评测文章 点我

vue.config.js配置

const externals = {
    vue: "Vue",
    "vue-router": "VueRouter",
    vuex: "Vuex",
    "mint-ui": "MINT",
    axios: "axios"

}

const cdn = {
    // 开发环境
    dev: {
        css: [
            "https://lib.baomitu.com/mint-ui/2.2.13/style.min.css"
        ],
        js: []
    },
    // 生产环境
    build: {
        css: [
            "https://lib.baomitu.com/mint-ui/2.2.13/style.min.css"
        ],
        js: [
            "https://lib.baomitu.com/vue/2.6.6/vue.min.js",
            "https://lib.baomitu.com/vue-router/3.0.1/vue-router.min.js",
            "https://lib.baomitu.com/vuex/3.0.1/vuex.min.js",
            "https://lib.baomitu.com/axios/0.18.0/axios.min.js",
            "https://lib.baomitu.com/mint-ui/2.2.13/index.js"
        ]
    }
}

configureWebpack: config => {
        if (isProduction) {
            // externals里的模块不打包
            Object.assign(config, {
                externals: externals
            })
       
        } else {
            // 为开发环境修改配置...
        }
    },
chainWebpack: config => {
    // 对vue-cli内部的 webpack 配置进行更细粒度的修改。
    // 添加CDN参数到htmlWebpackPlugin配置中, 详见public/index.html 修改
    config.plugin("html").tap(args => {
        if (process.env.NODE_ENV === "production") {
            args[0].cdn = cdn.build
        }
        if (process.env.NODE_ENV === "development") {
            args[0].cdn = cdn.dev
        }
        return args
    })
}




    
    
    
    
    
    
    
    <% for (var i in
    htmlWebpackPlugin.options.cdn&&htmlWebpackPlugin.options.cdn.css) { %>
    
    
    <% } %>

    vuedemo



    
    
<% for (var i in htmlWebpackPlugin.options.cdn&&htmlWebpackPlugin.options.cdn.js) { %> <% } %>
路由设计、登录拦截
const router = new Router({
    routes: [
        {
            path: "/",
            name: "home",
            component: Home,
            meta: {
                auth: false, // 是否需要登录
                keepAlive: true // 是否缓存组件
            }
        },
        {
            path: "/about",
            name: "about",
            component: () =>
                import(/* webpackChunkName: "about" */ "./views/About.vue"),
            meta: {
                auth: true,
                keepAlive: true
            }
        },
        {
            path: "/login",
            name: "login",
            component: () =>
                import(/* webpackChunkName: "login" */ "./views/login.vue"),
            meta: {
                auth: false,
                keepAlive: true
            }
        },
        {
            path: "*", // 未匹配到路由时重定向
            redirect: "/",
            meta: {
                // auth: true,
                // keepAlive: true
            }
        }
    ]
})

// 全局路由钩子函数 对全局有效
router.beforeEach((to, from, next) => {
    let auth = to.meta.auth
    let token = store.getters["login/token"];

    if (auth) { // 需要登录
        if (token) {
            next()
        } else {
            next({
                path: "/login",
                query: {
                    redirect: to.fullPath
                }
            })
        }
    } else {
        next()
    }
})

在meta中设置是否需要登录以及是否缓存当前组件,
在router.beforeEac路由钩子函数中对登录权限判断,没有登录的跳到登录页面,并且把当前页面传过去,登录后跳回这个页面。

对于页面缓存的在app.vue里进行处理


    

axios、api 设计

对于axios的设计主要是请求拦截器, respone拦截器,以及get,post的二次封装

axios.defaults.timeout = 12000 // 请求超时时间
axios.defaults.baseURL = process.env.VUE_APP_BASE_API

axios.defaults.headers.post["Content-Type"] =
    "application/x-www-form-urlencoded;charset=UTF-8" // post请求头的设置
// axios 请求拦截器
axios.interceptors.request.use(
    config => {
        // 可在此设置要发送的token
        let token = store.getters["login/token"];
        token && (config.headers.token = token)
        Indicator.open("数据加载中")
        return config
    },
    error => {
        return Promise.error(error)
    }
)
// axios respone拦截器
axios.interceptors.response.use(
    response => {
        // 如果返回的状态码为200,说明接口请求成功,可以正常拿到数据
        // 否则的话抛出错误 结合自身业务和后台返回的接口状态约定写respone拦截器
        Indicator.close()
        if (response.status === 200 && response.data.code === 0) {
            return Promise.resolve(response)
        } else {
            Toast({
                message: response.data.msg,
                position: "middle",
                duration: 2000
            });
            return Promise.reject(response)
        }
    },
    error => {
        Indicator.close()
        const responseCode = error.response.status
        switch (responseCode) {
            // 401:未登录
            case 401:
                break
            // 404请求不存在
            case 404:
                Toast({
                    message: "网络请求不存在",
                    position: "middle",
                    duration: 2000
                });
                break
            default:
                Toast({
                    message: error.response.data.message,
                    position: "middle",
                    duration: 2000
                });
        }
        return Promise.reject(error.response)
    }
)
/**
 * 封装get方法,对应get请求
 * @param {String} url [请求的url地址]
 * @param {Object} params [请求时携带的参数]
 */
function get (url, params = {}) {
    return new Promise((resolve, reject) => {
        axios
            .get(url, {
                params: params
            })
            .then(res => {
                resolve(res.data)
            })
            .catch(err => {
                reject(err.data)
            })
    })
    // 或者return axios.get();
}
/**
 * post方法,对应post请求
 * @param {String} url [请求的url地址]
 * @param {Object} params [请求时携带的参数]
 */
function post (url, params) {
    return new Promise((resolve, reject) => {
        axios
            .post(url, qs.stringify(params))
            .then(res => {
                resolve(res.data)
            })
            .catch(err => {
                reject(err.data)
            })
    })
    //  或者return axios.post();
}

为了方便管理api路径,这里把所以请求都放在了api文件夹下,如

import { get, post } from "@/axios/http.js"
function getIndex (params) {
    return get("/mock/5cb48c7ed491cd741c54456f/base/index", params)
}
function login(params) {
    return post("/mock/5cb48c7ed491cd741c54456f/base/login", params)
}
export {
    getIndex,
    login
}
其他 去除console.log

装uglifyjs-webpack-plugin插件

 // 上线压缩去除console等信息
config.plugins.push(
    new UglifyJsPlugin({
        uglifyOptions: {
            compress: {
                warnings: false,
                drop_console: true,
                drop_debugger: false,
                pure_funcs: ["console.log"] // 移除console
            }
        },
        sourceMap: false,
        parallel: true
    })
)
设置alias目录别名

在项目中经常会引用各个地方的文件,配置后可以更加方便的引入了

config.resolve.alias
            .set("assets", "@/assets")
            .set("components", "@/components")
            .set("view", "@/view")
            .set("style", "@/style")
            .set("api", "@/api")
            .set("store", "@/store")
环境变量和模式

在一个产品的前端开发过程中,一般来说会经历本地开发、测试脚本、开发自测、测试环境、预上线环境,然后才能正式的发布。对应每一个环境可能都会有所差异,比如说服务器地址、接口地址、websorket地址…… 等等。在各个环境切换的时候,就需要不同的配置参数,所以就可以用环境变量和模式,来方便我们管理。

.env                # 在所有的环境中被载入
.env.local          # 在所有的环境中被载入,但会被 git 忽略
.env.[mode]         # 只在指定的模式中被载入
.env.[mode].local   # 只在指定的模式中被载入,但会被 git 忽略

自定义的变量VUE_APP_开头,两个特殊的变量:

NODE_ENV - 会是 "development"、"production" 或 "test" 中的一个。具体的值取决于应用运行的模式。

BASE_URL - 会和 vue.config.js 中的 baseUrl 选项相符,即你的应用会部署到的基础路径。

如我们定义的.env

NODE_ENV = "development"
BASE_URL = "/"
VUE_APP_BASE_API = ""

.env.production

NODE_ENV = "production"
BASE_URL = "./"
VUE_APP_BASE_API = "https://easy-mock.com/"

在项目中可以用process.env.VUE_APP_*,如process.env.VUE_APP_BASE_API获取到定义的值

全局引入filter

把多个地方用到的过滤器写在一个js里面,复用代码。

// 过滤日期格式,传入时间戳,根据参数返回不同格式
const formatTimer = function(val, hours) {
    if (val) {
        var dateTimer = new Date(val * 1000)
        var y = dateTimer.getFullYear()
        var M = dateTimer.getMonth() + 1
        var d = dateTimer.getDate()
        var h = dateTimer.getHours()
        var m = dateTimer.getMinutes()
        M = M >= 10 ? M : "0" + M
        d = d >= 10 ? d : "0" + d
        h = h >= 10 ? h : "0" + h
        m = m >= 10 ? m : "0" + m
        if (hours) {
            return y + "-" + M + "-" + d + " " + h + ":" + m
        } else {
            return y + "-" + M + "-" + d
        }
    }
}
export default {
    formatTimer
}

main.js引入

import filters from "./filters/index"
// 注入全局过滤器
Object.keys(filters).forEach(item => {
    Vue.filter(item, filters[item])
})

使用

{{1555851774 | formatTimer()}}
vue中使用mock.js

查看我以前写的文章点击我

wepback的可视化资源分析工具插件---webpack-bundle-analyzer

用来分析哪些模块引入了哪些代码,进行有目的性的优化代码

在打包环境中加,使用命令npm run build --report

if (process.env.npm_config_report) {
    config.plugins.push(new BundleAnalyzerPlugin())
}

代码地址
项目地址: vue-cli3-H5

demo地址: https://zhouyupeng.github.io/vuecli3H5/#/

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/103791.html

相关文章

  • 基于vue-cli3.0项目工程重新构建空白版,拿来即用

    摘要:写在前面使用框架开发时,很多人会选择官方提供的脚手架,最新的已经更新到完全无配置,只需下载就能方便的使用构建的项目工程,但基础的并不能满足正常的项目开发,在开发中我们需要根据自己的习惯和业务功能而添加些基础功能。 写在前面 使用vue框架开发时,很多人会选择vue官方提供的cli脚手架,最新的cli已经更新到3.0完全无配置,只需下载就能方便的使用vuecli构建的项目工程,但基础的c...

    xingpingz 评论0 收藏0
  • 关于Vue2一些值得推荐文章 -- 五、六月份

    摘要:五六月份推荐集合查看最新的请点击集前端最近很火的框架资源定时更新,欢迎一下。苏幕遮燎沈香宋周邦彦燎沈香,消溽暑。鸟雀呼晴,侵晓窥檐语。叶上初阳乾宿雨,水面清圆,一一风荷举。家住吴门,久作长安旅。五月渔郎相忆否。小楫轻舟,梦入芙蓉浦。 五、六月份推荐集合 查看github最新的Vue weekly;请::点击::集web前端最近很火的vue2框架资源;定时更新,欢迎 Star 一下。 苏...

    sutaking 评论0 收藏0
  • 关于Vue2一些值得推荐文章 -- 五、六月份

    摘要:五六月份推荐集合查看最新的请点击集前端最近很火的框架资源定时更新,欢迎一下。苏幕遮燎沈香宋周邦彦燎沈香,消溽暑。鸟雀呼晴,侵晓窥檐语。叶上初阳乾宿雨,水面清圆,一一风荷举。家住吴门,久作长安旅。五月渔郎相忆否。小楫轻舟,梦入芙蓉浦。 五、六月份推荐集合 查看github最新的Vue weekly;请::点击::集web前端最近很火的vue2框架资源;定时更新,欢迎 Star 一下。 苏...

    khs1994 评论0 收藏0
  • 基于vue + element uitable编辑,保存和删除

    摘要:在最近年的工作中遇到过几次很基础的基于的开发简单的增删改查功能闲下来想整理一下以备后用架子用最新的此处可以参考我之前的最好的系列环境搭建篇项目基础构建好以后这个小我们暂且将数据存在之后我将更新基于前端操作数据库的操作点击新增按钮点击保存 在最近2年的工作中遇到过几次很基础的基于element-ui的table开发,简单的增删改查功能,闲下来想整理一下~ 以备后用 vue架子,用最新的...

    frontoldman 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<