精华内容
下载资源
问答
  • 晚上正准备关电脑早点溜街时,测试那边提了一个移动端访问H5页面无数据展示的bug,那一刻有些头疼,但是马上进入了调试状态。 首先打开了Fiddler抓包工具,把抓到的url复制到PC浏览器进行访问,结果发现PC上访问这个...

    思路:

    晚上正准备关电脑早点溜街时,测试那边提了一个移动端访问H5页面无数据展示的bug,那一刻有些头疼,但是马上进入了调试状态。

    首先打开了Fiddler抓包工具,把抓到的url复制到PC浏览器进行访问,结果发现PC上访问这个页面是有数据的,那就有些蛋疼了呀,今晚就得要发新版本,没想到最后还出这个幺蛾子,突然之前成为了全村的希望,感觉有些受宠若惊。

    冷静分析了下,与浏览器抓到的东西进行了比对,发现移动端是没有进行接口请求的,那估计是我的代码出了问题,推测是vue生命周期问题,但是在浏览器访问该页面的时候又没有报错。好吧,那就在项目加个打印工具(这里推荐使用eruda,不要使用vconsole,vconsole打印不全面)。发现是window.localStorage那个赋值地方出了问题,提示什么不能给localStorage的属性赋null,但实际上我赋的值是可以打印出来的,有判断了下window.localStorage,发现浏览器不支持window.localStorage,于是改变使用sessionStorage,问题解决了。这次的问题我感觉很奇葩,明明测试环境localStorage是支持的,到了正式环境就不支持了。

    解决方案:

    手机浏览器不支持localStorage,改用sessionStorage


    展开全文
  • 频繁访问接口会被限制访问,要过一会儿才能访问 这个版本无 eslint ,无额外配置,作为入门练习 这个版本是使用 vue-cli2 创建的,现在已经更新到 vue-cli4 了。 这个版本熟悉之后,有兴趣的可以看一下 ,使用 vue-...
  • 主要介绍了vue项目打包为APP,静态资源正常显示,但API请求不到数据的操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
  • 那如何在移动端访问本地的项目? 1、必需手机电脑在相同WiFi下 2、查看WiFi下的IP地址 (命令:ipconfig) 3、更改config.js文件 4、重新运行vue项目,域名已经更改,在移动端运行自己的项目吧! ...

    现如今移动端异常火爆,今天工作中需要写一个自定定位城市的功能,虽然浏览器也可以看到移动端的样式,但是自己心里还是不靠谱(devicemotion监听设备速度变化,deviceorientation监听设备方向变等事件)也必须在移动端预览效果

    那如何在移动端访问本地的项目?

    1、必需手机电脑在相同WiFi下
    2、查看WiFi下的IP地址 (命令:ipconfig)
    在这里插入图片描述
    3、更改config.js文件
    在这里插入图片描述

    4、重新运行vue项目,域名已经更改,在移动端运行自己的项目吧!

    展开全文
  • vue单页面实现pc移动端页面不同 博文:https://blog.csdn.net/qq_40488121/article/details/106218473
  • 创建一个完整的vue移动端项目配置

    万次阅读 多人点赞 2019-08-09 14:49:30
    vue的安装及创建一个新项目前一篇文章已经介绍过了,有需要的请看之前的文章 移动端项目 一、关于移动端的一些ui框架 1.Voinc 一个基于 vue.js 和 ionic 样式的 UI 框架...

    vue的安装及创建一个新项目前一篇文章已经介绍过了,有需要的请看之前的文章

    移动端项目

    一、关于移动端的一些ui框架
    1.Voinc 一个基于 vue.js 和 ionic 样式的 UI 框架(https://wangdahoo.github.io/vonic-documents/#/?id=%E4%BB%8B%E7%BB%8D)
    2.Vux 基于WeUI和Vue(2.x)开发的移动端UI组件库 (https://vux.li/#/?id=%E7%AE%80%E4%BB%8B)
    3. NutUI 京东轻量级移动端Vue组件库(https://nutui.jd.com/#/index)
    4. .Mint UI 由饿了么前端团队推出的 Mint UI (http://mint-ui.github.io/docs/#/zh-cn2)
    5. Vant是有赞前端团队基于有赞统一的规范实现的 Vue 组件库(https://github.com/youzan/zent)
    6. Cube UI 滴滴 WebApp实现的移动端组件库(https://didi.github.io/cube-ui/#/zh-CN/docs/quick-start)

    二、移动端适配

    方案:lib-flexible会自动在html的head中添加一个meta name="viewport"的标签,同时会自动设置html的font-size为屏幕宽度除以10,也就是1rem等于html根节点的font-size。使用postcss-px2rem-exclude自动将css中的px转成rem

    步骤:1.项目中引入lib-flexible npm install lib-flexible --save
    2.在项目的入口main.js文件中引入lib-flexible import ‘lib-flexible/flexible.js’
    3.安装postcss-px2rem-exclude npm install postcss-px2rem-exclude --save
    4.在项目的根目录下找到文件.postcssrc.js,在里面添加如下代码

    "postcss-px2rem-exclude": { 
          remUnit: 75,
          exclude: /node_modules|folder_name/i // 忽略node_modules目录下的文件(引入的第三方ui的样式不会随之改变)
        }
    
    

    5.在依赖文件node_modules中找到postcss-px2rem-exclude文件中的lib下的index.js添加以下代码(引入的第三方ui的样式不会随之改变)

    try {
          var flag = options.exclude.includes('/')
          if (flag) {
            var arr = options.exclude.split('/')
            options.exclude = new RegExp(arr[1], arr[2])
          }
        } catch (error) {}
    

    在这里插入图片描述

    二.一 安装sass-loader(如果使用到了sass编译再安装,不使用无需安装)

    npm install --save-dev sass-loader  
    //sass-loader依赖于node-sass  ,所以继续安装node-sass
    npm install --save-dev node-sass  
    //注意1.安装node-sass会报错的原因,一般是因为网慢,所以安装node-sass的时候可以使用cnpm安装
    //注意2.sass-loader安装了但是编译还是报错,原因可能是sass-loader版本的问题(8.0.0),在package.json中把sass-loader的版本改成7.3.1,然后删除依赖项,重新安装依赖
    

    三、给IDE装vue插件(我现在使用的是vscode,就以vscode为例)
    1.在vscode扩展里搜索Vetur
    在这里插入图片描述
    2.安装完成进行配置
    文件–>首选项–>用户代码片段–>点击新建代码片段–取名vue.json 确定
    3.删除多余代码
    4.粘贴下边的代码vue模板 (如果使用css预编译,则style的类型是scss,如不使用改为css)

    {
        "Print to console": {
            "prefix": "vue",   
            "body": [
                "<!-- $1 -->",
                "<template>",
                "<div class='$2'>$5</div>",
                "</template>",
                "",
                "<script>",
                "//这里可以导入其他文件(比如:组件,工具js,第三方插件js,json文件,图片文件等等)",
                "//例如:import 《组件名称》 from '《组件路径》';",
                "",
                "export default {",
                "//import引入的组件需要注入到对象中才能使用",
                "components: {},",
                "data() {",
                "//这里存放数据",
                "return {",
                "",
                "};",
                "},",
                "//监听属性 类似于data概念",
                "computed: {},",
                "//监控data中的数据变化",
                "watch: {},",
                "//方法集合",
                "methods: {",
                "",
                "},",
                "//生命周期 - 创建完成(可以访问当前this实例)",
                "created() {",
                "",
                "},",
                "//生命周期 - 挂载完成(可以访问DOM元素)",
                "mounted() {",
                "",
                "},",
                "beforeCreate() {}, //生命周期 - 创建之前",
                "beforeMount() {}, //生命周期 - 挂载之前",
                "beforeUpdate() {}, //生命周期 - 更新之前",
                "updated() {}, //生命周期 - 更新之后",
                "beforeDestroy() {}, //生命周期 - 销毁之前",
                "destroyed() {}, //生命周期 - 销毁完成",
                "activated() {}, //如果页面有keep-alive缓存功能,这个函数会触发",
                "}",
                "</script>",
                "<style lang='scss' scoped>",
                "//@import url($3); 引入公共css类",
                "$4",
                "</style>"
            ],
            "description": "Log output to console"
        }
    }
    

    6.上面代码中的 “prefix”: “vue”, 就是快捷键;保存好之后新建.vue结尾的文件试试(输入vue 按tab键就可以)

    在这里插入图片描述
    四、vue路由
    1.定义组件,刚刚已经用模板定义了一个组件
    2.在router index.js文件里引入刚刚写好的组件
    在这里插入图片描述
    我在这里是用的vue-router提供的按模块加载方式
    下面2行代码,没有指定webpackChunkName,每个组件打包成一个js文件。

    const ImportFuncDemo1 = () => import('../components/ImportFuncDemo1')
    const ImportFuncDemo2 = () => import('../components/ImportFuncDemo2')
    

    下面2行代码,指定了相同的webpackChunkName,会合并打包成一个js文件。

    // const ImportFuncDemo = () => import(/* webpackChunkName: 'ImportFuncDemo' */ '../components/ImportFuncDemo')
    // const ImportFuncDemo2 = () => import(/* webpackChunkName: 'ImportFuncDemo' */ '../components/ImportFuncDemo2')
    

    3.配置

    
    import Vue from "vue";
    import VueRouter from "vue-router";
    Vue.use(VueRouter)
    // 按模块加载...
    const HelloWorld= () => import('../components/HelloWorld')
    
    
     const routes=[{
        path: '/',
        redirect: '/HelloWorld',
        name:'HelloWorld'
      },{
        path:'/HelloWorld',
        component: HelloWorld,
        name:'HelloWorld'
      }
    ]
    
    const router = new VueRouter({
    	 routes,
       mode: 'history',
      //  vuex的严格模式
       strict: process.env.NODE_ENV !== 'production',
      // 这个整体做的是:在路由的history模式下,一些列表页利用缓存模式来记录位置(一般是返回不刷新,前进刷新),一般用了scrollBehavior,
      //同时还用keep-alive(缓存),activated(缓存下触发的钩子)配合做列表页的返回记录位置。缓存模式也有坑,就是何时清除缓存,一般是从新进入页面就清除。
      //回到主题,滚动行为就是:例如一个列表页,滑动了很多,点进去、再返回记录刚刚的位置
    	scrollBehavior (to, from, savedPosition) {
    	    if (savedPosition) {
    		    return savedPosition
    		} else {
    			if (from.meta.keepAlive) {
    				from.meta.savedPosition = document.body.scrollTop;
    			}
    		    return { x: 0, y: to.meta.savedPosition ||0}
    		}
    	}
    })
    
    export default router
    
    

    4.路由文件注入到main.js文件中

    import Vue from 'vue';
    import router from './router/index';
    import 'lib-flexible/flexible.js';
    import App from './App'
    
    
    new Vue({
      el: '#app',
      router,
      components: { App },
      template: '<App/>'
    })
    
    

    5.在 app.vue里配置router-view

    <template>
      <div id="app">
        <router-view></router-view>
      </div>
    </template>
    

    6.路由跳转及传参方式

     1.$router为VueRouter实例,想要导航到不同URL,则使用$router.push方法
     2.$route为当前router跳转对象,里面可以获取name、path、query、params等
    
    
    (1)设置动态路由
            {
        		path:'/shopDetails/:id',
       	 		component: shopDetails,
       			 name:'shopDetails'
             }
            跳转this.$router.push('/shopDetails/'+id)
            获取id通过   this.$route.params.id
           
    (2)通过params携带参数 ,路由属性中的name来匹配路由    
            
             {
        	 	path:'/shopDetails',
    		   	 component: shopDetails,
    		   	 name:'shopDetails'
             }
             跳转 this.$router.push({name:'shopDetails',params:{id:id}})  
             获取  this.$route.params.id
    (3)通过path匹配路由,query携带参数 这种情况下 query传递的参数会显示在url后面?id=?
             {
    	    	 path:'/shopDetails',
    		   	 component: shopDetails,
    		   	 name:'shopDetails'
             }
             跳转 this.$router.push({path:'/shopDetails',query:{id:id}})  
             获取  this.$route.query.id
    

    7.query和params的区别

    1.动态路由和query属性传值 页面刷新参数不会丢失, params会丢失 2.动态路由一般用来传一个参数时居多(如详情页的id), query、params可以传递一个也可以传递多个参数 。
    2.直白的来说query相当于get请求,页面跳转的时候,可以在地址栏看到请求参数,而params相当于post请求,参数不会再地址栏中显示

    8.路由的模式

    五、axios请求

    axios文档上的教程在这里就不重复叙述了,需要的看文档,我在这里贴下我的使用教程
    axios文档地址:https://www.npmjs.com/package/axios

    1.安装 npm install axios
    2.在utils下新建一个axios文件用来配置axios

    import axios from 'axios';
    const qs = require('qs');
    const service=axios.create({
        baseURL: process.env.BASE_API,    //请求公共地址,baseURL`将被添加到`url`,所以后边请求只需api的方式即可
        timeout: 5000,    //请求响应时间
    })
    // 是否携带cookie信息,默认为false,根据项目需求设置
    service.defaults.withCredentials = true;
    // 添加一个请求拦截器
    service.interceptors.request.use(function (config) {
        // 对请求数据做些事
        if(config.method  === 'post'){  //post传参序列化
          config.data = qs.stringify(config.data);
        } 
        return config;
      }, function (error) {
        return Promise.reject(error);
      });
    
    // 添加一个响应拦截器
    service.interceptors.response.use(function (response) {
        // 对响应数据做些事
        return response;
      }, function (error) {
          console.log(error)
        // Do something with response error
        return Promise.reject(error);
      });
    export default  service;
    

    3.新建一个api文件,这里边存放所有的请求函数,方便查找管理

    import axios from '../utils/axios';
    // 首页banner
    export function banner(){
        return axios({
            url:'/wxchat/xxx.action', //请求的地址
            method:'POST'
        })
    }
    // post请求带参数
    export function qiang(activityStatusType){
        return axios({
            url:'/wxchat/xxx.action',
            method:'POST',
            data:{activityStatusType:activityStatusType}
        })
    }
    // get请求
    export function moneys(){
    	 return axios({
            url: '/sd-web/xxx',
            method: 'get'
        }); 
    }
    // get请求带参数
    export function moneys(ids){
    	 return axios({
            url: '/sd-web/xxx',
            method: 'get',
            params:{id:ids}
        }); 
    }
    

    4.页面实际应用

     import {banner} from '../../api/api'; //在页面上引入需要的请求函数
      //在生命周期函数或者需要的方法里运用
        mounted() {
            banner().then(response => {
                this.banner=response.data.data.SlAdvertisTabList
            }).catch(err=>{
                 console.log(err)
            });
        },
     
    

    5.跨域配置

    (1)在config文件夹写的index.js里配置跨域
    在这里插入图片描述

    proxyTable: {
        '/wxchat': {
          target: 'xxx',  //目标接口域名
          changeOrigin: true,  //是否跨域
          secure: false,  //target默认情况下,不接受运行在HTTPS上,且使用了无效证书的后端服务器。如果你想要接受, 则需设置该项为false
          // pathRewrite: { // 如果接口本身没有/wxchat需要通过pathRewrite来重写了地址      重写接口
          //   '^/wxchat: ' '   
          // }
        }
    },
    

    注:我写的这个项目里,本身存在/wechat通用前缀,所以我没有用pathRewrite重写,如果你们接口里没有通用前缀的话,是要进行重写的

    上面配置中,’^/wxchat’ 其实是一个正则表达式

    ‘^/wxchat’ 应该拆分成 ‘^’ 和 ‘/wxchat’ 两个字符串,其中 ‘^’ 匹配的是字符串最开始的位置。

    ‘/wxchat’: {}, 就是告诉node, 我接口只要是’/wxchat’开头的才用代理.所以你的接口就要这么写/wxchat/xx/xx. 最后代理的路径就是 http://xxx.xx.com/wxchat/xx/xx.
    可是不对啊, 我正确的接口路径里面没有/wxchat啊. 所以就需要 pathRewrite,用’’^/wxchat’’:’’, 把’/wxchat’去掉, 这样既能有正确标识, 又能在请求接口的时候去掉wxchat

    接口没有通用前缀的开发环境的配置

    'use strict'
    const merge = require('webpack-merge')
    const prodEnv = require('./prod.env')
    
    module.exports = merge(prodEnv, {
      NODE_ENV: '"development"',
      BASE_API: '"/wechat"',     //在跨域里配置的//wechat,接口里就不用写通用前缀了,就按照正常接口写就可以了
    })
    

    接口有通用前缀的开发环境配置

    'use strict'
    const merge = require('webpack-merge')
    const prodEnv = require('./prod.env')
    
    module.exports = merge(prodEnv, {
      NODE_ENV: '"development"',
      BASE_API: '""',     //这里为空就好了,通用前缀就写在接口里
    })
    

    六、vuex的使用

    关于vuex的使用 ,什么时候该使用vuex? 当你觉得需要全局的状态,但是通过别的方式又太麻烦的时候,你就需要了,比如说我现在做的一个app项目,header组件是一个公共组件,那么header组件里边的标题怎么能随着页面的跳转而改变呢,vuex就可以做到 (个人理解,有不同理解的可以评论)
    缺点:vuex页面重新刷新,数据会丢失 解决方法:存本地,state数据可以从本地获取

    1.vuex中,有默认的五种基本的对象:

    • state 全局要访问的值
    • getters 实时监听state值的变化,对数据获取之前的再次编译,可以理解为state的计算属性。
    • mutations 改变state里的初始值 同步的
    • actions 异步触发mutations里面的方法
    • modules:store的子模块,为了开发大型项目,方便状态管理而使用的。这里我们就不解释了,用起来和上面的一样。

    2.下载安装vuex npm install vuex --save
    3.在src下新建一个store文件夹创建一个index.js 用来配置.
    4.引入vue,vuex,使用use全局注入插件

    import Vue from 'vue';
    import Vuex from 'vuex';
    Vue.use(Vuex);
    const store=new Vuex.Store({
        // 设置全局要访问的state值
        state:{
           title:'测试标题',    //头部标题
        }
    })
    export default store;
    

    5.在main.js里引入store,并全局注入

    import Vue from 'vue';
    import router from './router/index';
    import store from './store';
    import 'lib-flexible/flexible.js';
    import App from './App'
    
    
    new Vue({
      el: '#app',
      router,
      store,
      components: { App },
      template: '<App/>'
    })
    
    

    6.在任意组件内测试刚刚写的标题

    <template>
        <div class='orderDetails'>
            {{$store.state.title}}
        </div>
    </template>
    

    7.如图所示,刚刚的标题已经设置成功了
    在这里插入图片描述
    8.上图所示已经成功设置了标题,那怎么让它的标题改变呢,就要用到mutations对象了,我们在mutations对象里定义一个改变标题的方法,mutations里面的参数,第一个默认为state,接下来的为自定义参数。

     // 改变state里的初始值 同步的
        mutations :{
            TITLE(state,title){
                return state.title=title
            },
        },
    

    9.看下测试结
    在这里插入图片描述在这里插入图片描述
    9.从图上可以看出这个已经实现了,接下来看异步操作

     // 异步触发mutations里面的方法 在外部组件里进行全局执行actions里面方法的时候,你只需要用执行this.$store.dispatch('title',132) 这样就可以全局改变改变标题的值了
        actions:{
           title({commit},title){
                commit('TITLE',title)
            },
        }
    

    就不上图了,跟之前同步操作是一样的结果

    10.vuex同步和异步的区别

    1、当点发送过快,页面中渲染的内容与state中的数据不一致,vuex里面的state变得慢,且不持续更新
    2、action中是可以做到页面中state中数据保持一致
    3、当你的操作行为中含有异步操作,比如向后台发送请求获取数据,就需要使用action的dispatch去完成了。其他使用commit即可。

    七、打包上线
    1.

    执行  npm run build 命令
    

    2.执行完成后,你会发现你的目录下多了一个dist的文件夹,这个就是打包的数据

    3.打包之后出现页面空白的原因
    3.1 css,js路径引用错误的问题

    解决:到config文件夹中打开index.js文件。
    文件里面有两个assetsPublicPath属性,更改第一个,也就是更改build里面的assetsPublicPath属性:
    assetsPublicPath属性作用是指定编译发布的根目录,‘/’指的是项目的根目录 ,’./’指的是当前目录。
    在这里插入图片描述

    3.2 设置路由history模式

    解决:改为hash或者直接把模式配置删除,让它默认的就行 。如果非要使用history模式的话,需要你在服务端加一个覆盖所有的情况的候选资源:如果URL匹配不到任何静态资源,则应该返回一个index.html,这个页面就是你app依赖页面。

    3.3 在css中引入的背景图片的路径问题

    解决:到build文件夹中打开util.js文件,添加路径代码

    在这里插入图片描述

    展开全文
  • 做了很长一段时间的Vue项目,现在抽出空来做个小结,以便之后查看,回顾: 1.开发规范 对代码风格、命名规则、目录结构进行统一规范。 静态资源的使用规范。 单元测试、提交线上测试规范。 Git ...

    做了很长一段时间的Vue项目,现在抽出空来做个小结,以便之后查看,回顾:

    1.开发规范

    • 对代码风格、命名规则、目录结构进行统一规范。

    • 静态资源的使用规范。

    • 单元测试、提交线上测试规范。

    • Git 提交记录和多人协作规范。

    2.样式

    • 2.1  CSS 预处理器的选择

      • Sass/Scss ✅

      • Less ✅

      • Stylus ⭕

    • 2.2  局部样式与全局样式

      • 局部样式(一般都是使用 scoped 方案):

    <style lang="scss" scoped>
      ...
    </style>
    
    • 全局样式      

              全局样式 目录:@/styles

             variable.scss: 全局变量管理 mixins.scss: 全局 Mixins 管理 global.scss: 全局样式

            其中 variable.scss 和 mixins.scss 会优先于 global.css 加载,并且可以不通过 import 的方式在项目中任何位置使用这些变量和 mixins。

    // vue.config.js
    module.exports = {
      css: {
        loaderOptions: {
          sass: {
            prependData: `
            @import '@/styles/variable.scss';
            @import '@/styles/mixins.scss';
            `,
          },
        },
      },
    }
    • 2.3 体验优化

    页面载入进度条:

    使用 nprogress 对路由跳转时做一个伪进度条,这样做在网络不好的情况下可以让用户知道页面已经在加载了:

    import NProgress from 'nprogress';
    
    router.beforeEach(() => {
      NProgress.start();
    });
    
    router.afterEach(() => {
      NProgress.done();
    });

    美化滚动条:

    一直用 Mac 做前端,突然发现同事的 Windows 上出现了十分丑陋的滚动条,为了保持一致:

    ::-webkit-scrollbar {
      width: 6px;
      height: 6px;
    }
    
    ::-webkit-scrollbar-track {
      width: 6px;
      background: rgba(#101F1C, 0.1);
      -webkit-border-radius: 2em;
      -moz-border-radius: 2em;
      border-radius: 2em;
    }
    
    ::-webkit-scrollbar-thumb {
      background-color: rgba(#101F1C, 0.5);
      background-clip: padding-box;
      min-height: 28px;
      -webkit-border-radius: 2em;
      -moz-border-radius: 2em;
      border-radius: 2em;
    }
    
    ::-webkit-scrollbar-thumb:hover {
      background-color: rgba(#101F1C, 1);
    }

    静态资源加载页面:

    首次加载页面时,会产生大量的白屏时间,这时做一个 loading 效果看起来会很友好,其实很简单,直接在 public/index.html 里写一些静态的样式即可。

    3.移动端 100vh 问题

    在移动端使用 100vh 时,发现在 Chrome、Safari 浏览器中,因为浏览器栏和一些导航栏、链接栏导致不一样的呈现:

    你以为的 100vh === 视口高度

    实际上 100vh === 视口高度 + 浏览器工具栏(地址栏等等)的高度

    解决方案:

    安装 vh-check npm install vh-check \--save

    import vhCheck from 'vh-check';
    vhCheck('browser-address-bar');

    定义一个 CSS Mixin

    @mixin vh($height: 100vh) {
      height: $height;
      height: calc(#{$height} - var(--browser-address-bar, 0px));
    }

    4.组件库

    因为 Element UI 长期没更新,并且之前使用过 React 的 Ant Design(重点),所以组件库选择了Ant Design Vue。

    • 4.1 使用 .less 文件

    Ant Design Vue 的样式使用了 Less 作为开发语言,并定义了一系列全局/组件的样式变量,所以需要安装了 less、less-loader,在 @/styles/antd-theme.less 可以覆盖默认样式。

    优点是:

    方便快捷,可以修改 class,覆盖默认变量。

    缺点是:

    必须引入 @import '~ant-design-vue/dist/antd.less'; ,引入后会将所有的组件样式全部引入,导致打包后的 css 体积达到 500kb 左右。

    • 4.2.使用 JavaScript 对象

    通过 JavaScript 对象的方式可以修改内置变量,需要对 Less 进行配置:

    // vue.config.js
    const modifyVars = require('./src/styles/antdTheme.js');
    
    module.exports = {
      css: {
        loaderOptions: {
          less: {
            lessOptions: {
              javascriptEnabled: true,
              modifyVars,
            },
          },
        },
      },
    }

    这一步还可以继续优化,通过 babel-plugin-import 使 Ant Design Vue 的组件样式可以按需加载:

    // babel.config.js
    module.exports = {
      presets: [
        '@vue/cli-plugin-babel/preset',
      ],
      plugins: [
        [
          'import',
          { libraryName: 'ant-design-vue', libraryDirectory: 'es', style: true },
        ],
      ],
    };

    优点是:

    可以按需引入,打包后的 CSS 体积取决于你引用了多少个组件。

    缺点是:

    不能使用 class 进行样式覆盖。

    • 4.3 干掉无用的图标

    Ant Design Vue 把所有的 Icon 一次性引入(不管你因用了多少个组件),这使得体积打包后图标所占的体积竟然有几百 kb 之多。这些图标大多数不会被设计师所采纳,所以部分图标都应该被干掉:

    创建一个 icons.js 来管理 Ant Design Vue 图标,这里以一个 Loading 图标为例:

    // @/src/assets/icons.js
    export { default as LoadingOutline } from '@ant-design/icons/lib/outline/LoadingOutline';

    如何知道你要加载的图标在什么路径下?

    在 @ant-design/icons/lib 目录下有三种风格的图标,分别是 fill、outline、twotone,这里面内部的文件并不是 svg 格式,而是 js 和 ts 格式,这就是为什么我们可以这么引入图标的关键所在了。

    下一步是通过配置 vue.config.js 将这个文件引入进来:

    // vue.config.js
    module.exports = {
      configureWebpack: {
        resolve: {
          alias: {
            '@ant-design/icons/lib/dist$': path.resolve(__dirname, './src/assets/icons.js'),
          },
        },
      },
    }
    • 4.4解决 Moment 多国语

    解决到这之后,Ant Design Vue 居然还很大,这是因为 moment 是 Ant Design Vue 中有强依赖该插件,所以使用 webpack 插件减小打包体积,这里我们只保留 zh-cn 语言包:

    // vue.config.js
    module.exports = {
      chainWebpack: (config) => {
        config
          .plugin('ContextReplacementPlugin')
          .use(webpack.ContextReplacementPlugin, [/moment[/\\]locale$/, /zh-cn/]);
      },
    }
    • 4.5 部分组件需要在页面内引用

    Ant Design Vue 中部分体积较大的组件,例如 DatePicker,根据业务需求,应考虑在页面中进行加载,尽量保证首屏加载的速度:

    <script>
    import { DatePicker } from 'ant-design-vue';
    export default {
      components: {
        ADatePicker: DatePicker,
      },
    }
    </script>

    5.静态资源与图标

    5.1静态资源

    所有的静态资源文件都会上传到 阿里云 OSS 上,所以在环境变量上加以区分。

    .env.development 与 .env.production 的 VUE_APP_STATIC_URL 属性分别配置了本地的静态资源服务器地址和线上 OSS 的地址。

    本地的静态资源服务器是通过 pm2 + http-server 创建的,设计师切完直接扔进来就好了。

    5.2 自动注册 Svg 图标

    在日常的开发中,总是会有着大量的图标需要使用,这里我们直接选择使用 SVG 图标。但是如果每次使用图标还需要通过路径找到这张图标岂不是很麻烦?

    下面这种才是我想要的方案(直接 name 等于 文件名即可):

    <template>
    	<svg name="logo" />
    </template>

    而且最后打包后需要合并成一张雪碧图。

    首先需要对 @/assets/icons 文件夹下的 svg 图标进行自动注册,需要对 webpack 和 svg-sprite-loader 进行了相关设置,文件全部打包成 svg-sprite。

    module.exports = {
      chainWebpack: (config) => {
        config.module
          .rule('svg')
          .exclude.add(resolve('src/assets/icons'))
          .end();
    
        config.module
          .rule('icons')
          .test(/\.svg$/)
          .include.add(resolve('src/assets/icons'))
          .end()
          .use('svg-sprite-loader')
          .loader('svg-sprite-loader');
      },
    }

    写一个全局用的 Vue 组件 <m-svg />:

    // @/components/m-svg/index.js

    const requireAll = (requireContext) => requireContext.keys().map(requireContext);
    const req = require.context('@/assets/icons', false, /\.svg$/);
    requireAll(req);

    @/components/m-svg/index.vue

    <template>
      <svg class="mw-svg" aria-hidden="true">
        <use :xlink:href="iconName"></use>
      </svg>
    </template>
    <script>
    export default {
      name: 'm-svg',
      props: {
        name: { type: String, default: '' },
      },
      computed: {
        iconName() {
          return `#${this.name}`;
        },
      },
    };
    </script>
    <style lang="scss" scoped>
    .mw-svg {
      width: 1.4em;
      height: 1.4em;
      fill: currentColor;
      overflow: hidden;
      line-height: 1em;
      display: inline-block;
    }
    </style>

    参数 name

    • 类型:String

    • 默认值:null

    • 说明:放置在 @/assets/icons 文件夹下的文件名

    样式

    • 图标的大小可以通过 width + height 属性改变。

    • 通过改变 font-size 属性改变,宽高 = font-zise * 1.4

    6.异步请求

    6.1 封装 Axios

    在 @/libs/request.js 路径下对 Axios 进行封装,封装了请求参数,请求头,以及错误提示信息、 request 拦截器、response 拦截器、统一的错误处理、baseURL 设置等。

    废话不说直接贴代码:

    import axios from 'axios';
    import get from 'lodash/get';
    import storage from 'store';
    // 创建 axios 实例
    const request = axios.create({
     // API 请求的默认前缀
     baseURL: process.env.VUE_APP_BASE_URL,
     timeout: 10000, // 请求超时时间
    });
    
    // 异常拦截处理器
    const errorHandler = (error) => {
     const status = get(error, 'response.status');
     switch (status) {
       /* eslint-disable no-param-reassign */
       case 400: error.message = '请求错误'; break;
       case 401: error.message = '未授权,请登录'; break;
       case 403: error.message = '拒绝访问'; break;
       case 404: error.message = `请求地址出错: ${error.response.config.url}`; break;
       case 408: error.message = '请求超时'; break;
       case 500: error.message = '服务器内部错误'; break;
       case 501: error.message = '服务未实现'; break;
       case 502: error.message = '网关错误'; break;
       case 503: error.message = '服务不可用'; break;
       case 504: error.message = '网关超时'; break;
       case 505: error.message = 'HTTP版本不受支持'; break;
       default: break;
       /* eslint-disabled */
     }
     return Promise.reject(error);
    };
    
    // request interceptor
    request.interceptors.request.use((config) => {
     // 如果 token 存在
     // 让每个请求携带自定义 token 请根据实际情况自行修改
     // eslint-disable-next-line no-param-reassign
     config.headers.Authorization = `bearer ${storage.get('ACCESS_TOKEN')}`;
     return config;
    }, errorHandler);
    
    // response interceptor
    request.interceptors.response.use((response) => {
     const dataAxios = response.data;
     // 这个状态码是和后端约定的
     const { code } = dataAxios;
     // 根据 code 进行判断
     if (code === undefined) {
       // 如果没有 code 代表这不是项目后端开发的接口
       return dataAxios;
     // eslint-disable-next-line no-else-return
     } else {
       // 有 code 代表这是一个后端接口 可以进行进一步的判断
       switch (code) {
         case 200:
           // [ 示例 ] code === 200 代表没有错误
           return dataAxios.data;
         case 'xxx':
           // [ 示例 ] 其它和后台约定的 code
           return 'xxx';
         default:
           // 不是正确的 code
           return '不是正确的code';
       }
     }
    }, errorHandler);
    
    export default request;
    • 通过 VUE_APP_BASE_URL 区分线上与开发环境的 API 地址。

    • code 起到一个比较关键的作用,例如 token 过期时的验证。

    • 使用了一个叫 sotre 的包作为本地储存的工具用来存储 token。

    6.2 跨域问题

    跨域问题一般情况直接找后端解决了,你要是不好意思打扰他们的话,可以用 devServer 提供的 proxy 代理:

    // vue.config.js
    devServer: {
      proxy: {
        '/api': {
          target: 'http://47.100.186.132/your-path/api',
          ws: true,
          changeOrigin: true,
          pathRewrite: {
            '^/api': ''
          }
        }
      }
    }

    6.3 Mock 数据

    一个很常见的情况,后端接口没出来,前端在这干瞪眼。

    Mock 数据功能是基于 mock.js (opens new window)开发,通过 webpack 进行自动加载 mock 配置文件。

    规则

    • 所有的 mock 配置文件均应放置在 @/mock/services 路径内。

    • 在 @/mock/services 内部可以建立业务相关的文件夹分类存放配置文件。

    • 所有的配置文件应按照 ***.mock.js 的命名规范创建。

    • 配置文件使用 ES6 Module 导出 export default 或 export 一个数组。

    入口文件

    import Mock from 'mockjs';
    
    Mock.setup({
      timeout: '500-800',
    });
    
    const context = require.context('./services', true, /\.mock.js$/);
    
    context.keys().forEach((key) => {
      Object.keys(context(key)).forEach((paramKey) => {
        Mock.mock(...context(key)[paramKey]);
      });
    });

    示例模板

    import Mock from 'mockjs';
    
    const { Random } = Mock;
    
    export default [
      RegExp('/example.*'),
      'get',
      {
        'range|50-100': 50,
        'data|10': [
          {
            // 唯一 ID
            id: '@guid()',
            // 生成一个中文名字
            cname: '@cname()',
            // 生成一个 url
            url: '@url()',
            // 生成一个地址
            county: Mock.mock('@county(true)'),
            // 从数组中随机选择一个值
            'array|1': ['A', 'B', 'C', 'D', 'E'],
            // 随机生成一个时间
            time: '@datetime()',
            // 生成一张图片
            image: Random.dataImage('200x100', 'Mock Image'),
          },
        ],
      },
    ];

    6.4路由

           6.4.1  Layout

    布局暂时分为三大类:

    • frameIn:基于 BasicLayout,通常需要登录或权限认证的路由。

    • frameOut:不需要动态判断权限的路由,如登录页或通用页面。

    • errorPage:例如404。

          6.4.2  权限验证

    通过获取当前用户的权限去比对路由表,生成当前用户具的权限可访问的路由表,通过 router.addRoutes 动态挂载到 router 上。

    • 判断页面是否需要登陆状态,需要则跳转到 /user/login

    • 本地存储中不存在 token 则跳转到 /user/login

    • 如果存在 token,用户信息不存在,自动调用 vuex '/system/user/getInfo'

    在路由中,集成了权限验证的功能,需要为页面增加权限时,在 meta 下添加相应的 key:

    auth

    • 类型:Boolean

    • 说明:当 auth 为 true 时,此页面需要进行登陆权限验证,只针对 frameIn 路由有效。

    permissions

    • 类型:Object

    • 说明:permissions 每一个 key 对应权限功能的验证,当 key 的值为 true 时,代表具有权限,若 key 为 false,配合 v-permission 指令,可以隐藏相应的 DOM。

    在这里贴一下路由跳转时权限验证的代码:

    import router from '@/router';
    import store from '@/store';
    import storage from 'store';
    import util from '@/libs/utils';
    
    // 进度条
    import NProgress from 'nprogress';
    import 'nprogress/nprogress.css';
    
    const loginRoutePath = '/user/login';
    const defaultRoutePath = '/home';
    
    /**
     * 路由拦截
     * 权限验证
     */
    router.beforeEach(async (to, from, next) => {
      // 进度条
      NProgress.start();
      // 验证当前路由所有的匹配中是否需要有登录验证的
      if (to.matched.some((r) => r.meta.auth)) {
        // 是否存有token作为验证是否登录的条件
        const token = storage.get('ACCESS_TOKEN');
        if (token && token !== 'undefined') {
          // 是否处于登录页面
          if (to.path === loginRoutePath) {
            next({ path: defaultRoutePath });
            // 查询是否储存用户信息
          } else if (Object.keys(store.state.system.user.info).length === 0) {
            store.dispatch('system/user/getInfo').then(() => {
              next();
            });
          } else {
            next();
          }
        } else {
          // 没有登录的时候跳转到登录界面
          // 携带上登陆成功之后需要跳转的页面完整路径
          next({
            name: 'Login',
            query: {
              redirect: to.fullPath,
            },
          });
          NProgress.done();
        }
      } else {
        // 不需要身份校验 直接通过
        next();
      }
    });
    
    router.afterEach((to) => {
      // 进度条
      NProgress.done();
      util.title(to.meta.title);
    });

    6.5 页面开发

    • 根据业务需要划分,按照路由层级在 views 中创建相对应的页面组件,以文件夹的形式创建,并在文件夹内创建 index.vue 文件作为页面的入口文件。

    • 页面内的组件:在页面文件夹下创建 components 文件夹,在其内部对应创建相应的组件文件,如果是复杂组件,应以文件夹的形式创建组件。

    • 工具模块:能够高度抽象的工具模块,应创建在 @/src/libs 内创建 js 文件。

    7、构建优化

    7.1 包分析工具

    构建代码之后,到底是什么占用了这么多空间?靠直觉猜测或者使用 webpack-bundle-analyzer。

    const WebpackBundleAnalyzer = require('webpack-bundle-analyzer');
    
    module.exports = {
      chainWebpack: (config) => {
        if (process.env.use_analyzer) {
          config
            .plugin('webpack-bundle-analyzer')
            .use(WebpackBundleAnalyzer.BundleAnalyzerPlugin);
        }
      },
    };

    7.2 开启 Gzip

    对,这这么一句话,后端就得支持你的 .gz 文件了,而你只需要坐着等老板夸:

    chainWebpack: (config) => {
      config
        .plugin('CompressionPlugin')
        .use(CompressionPlugin, []);
    },

    7.3 路由懒加载

    这块 @vue/cli 已经帮忙处理好了,但也需要了解一下他的原理和如何配置。

    {
      path: 'home',
      name: 'Home',
      component: () => import(
        /* webpackChunkName: "home" */ '@/views/home/index.vue'
      ),
    },

    webpackChunkName 这条注释还是很有必要加的,至少你打包后知道又是哪个页面变得又臭又大。

    8.Vue 代码规范

    常规

    • 当在组件中使用 data 属性的时候 (除了 new Vue 外的任何地方),它的值必须是返回一个对象的函数 data() { return {...} }

    • prop 的定义应该尽量详细,至少需要指定其类型。

    • 布尔类型的 attribute, 为 true 时直接写属性值。

    • 不要在computed中对vue变量进行操作。

    • 应该优先通过 prop 和事件进行父子组件之间的通信,而不是 this.$parent 或改变 prop。

    • 在组件上总是必须用 key 配合 v-for,以便维护内部组件及其子树的状态。

    • v-if 和 v-for 不能同时使用

    • 公共方法尽量不要挂到原型上, 可以写在 utils 文件,也可以使用 mixin 文件。不要将业务公共组件注册到全局。

    • 不要将任何第三方插件挂载到 vue 原型上。

    • 具有高度通用性的方法,要封装到 libs、全局组件或指令集里。

    • 为组件样式设置作用域。

    • 尽量使用指令缩写。

    vuex

    State (opens new window)为单一状态树,在 state 中需要定义我们所需要管理的数组、对象、字符串等等,只有在这里定义了,在 vue 的组件中才能获取你定义的这个对象的状态。

    • 修改 state 中数据必须通过 mutations

    • 每一个可能发生改变的 state 必须同步创建一条或多条用来改变它的 mutations

    • 服务端获取的数据存放在 state 中,作为原始数据保留,不可变动。

    Getters (opens new window)有点类似 vue.js 的计算属性,当我们需要从 store 的 state 中派生出一些状态,那么我们就需要使用 getters,getters 会接收 state 作为第一个参数,而且 getters 的返回值会根据它的依赖被缓存起来,只有 getters 中的依赖值(state 中的某个需要派生状态的值)发生改变的时候才会被重新计算。

    • 通过 getters 处理你需要得到的数据格式,而不是通过修改 state 原始数据。

    • 组件内不强制使用 mapGetters,因为你可能需要使用 getter 和 setter

    • 改变 state 的唯一方法就是提交 mutations (opens new window)。

    • 组件内使用 mapMutations 辅助函数将组件中的 methods 映射为 store.commit 调用。

    • 命名采用 大写字母 + 下划线 的规则。

    • 定义 CLEAR,以确保路由切换时可以初始化数据。

    Actions

    • 页面重的数据接口尽量在 actions (opens new window)中调用。

    • 服务端返回的数据尽量不作处理,保留原始数据。

    • 获取到的数据必须通过调用 mutations 改变 state

    Modules

    • 通常情况下按照页面划分 modules (opens new window)。

    • 默认内置了 system 保证了脚手架的基础功能。

    • 每个页面模块或页面的子模块添加属性 namespaced: true

    展开全文
  • vue项目实战】Vue工程化项目--猫眼电影移动端

    万次阅读 多人点赞 2019-04-16 13:42:46
    使用 vue-cli 创建项目。现在先把主框架搭起来,熟悉路由和组件,后面在 项目demo 持续贴出完整项目代码。 vue初始化项目 首先全局安装Vue脚手架——vue-cli npm/cnpm install vue-cli -g 新建一个文件夹vue-demo...
  • 首先第一步:找到vue项目config文件夹下的index.js 第二步:找到dev这个对象里的host的参数,改成你当前电脑的IP地址 注意:不要加上http,否则会报错 cmd 打开命令运行符,输入“ipconfig”查询本地 IP; 第三...
  • 在很多情况下,开发移动端项目 为了避免模拟器与真机之间的差异,大部分情况需要使用真机进行实测。 非脚手架项目的手机实时预览方法相对比较简单,请参阅我的另一篇文章【移动端移动端项目手机预览方法 而...
  • Localhost及ip地址访问Vue项目移动端) (1)将host:'localhost’改为host:‘ip地址’ (2)在浏览器里面访问 (3)将浏览器ip地址转换为二维码,用手机扫描(手机需和电脑在同一个局域网内) ...
  • Vue项目实战——仿每日优鲜(移动端网站)

    万次阅读 多人点赞 2019-06-29 18:58:03
    项目描述:一个基于优鲜食物的移动端购物网站(大部分功能模块已实现,适合有一定Vue基础的人学习) 技术栈:Vue CLI+Vue Router+Vuex+Less+Mint UI+axios+Swiper+Animate+Better Scroll+Mock+Vue Touch
  • 解决移动端vue项目资源缓存问题

    千次阅读 2019-12-18 16:01:32
    我们希望每次进入项目都会重新加载资源配置,以防配置文件修改而导致页面没有更新问题 我们习惯通过在资源路径加上时间戳等参数来保证每次进入的正常刷新 但是,vue中要怎么处理呢,代码如下: index.html: <...
  • 案例 -- web移动端项目vue

    多人点赞 2021-11-03 10:03:57
    开发流程 选择手动模式 安装vue扩展 ...Vant - Mobile UI Components built on Vue ...在项目根目录下面有一个babel.config.js文件,在此文件中进行如下操作 修改babel.config.js文件后,一定要重...
  • vue项目移动端的调试

    千次阅读 2018-07-30 21:31:59
    首要条件:同一局域网下(大致理解为链接相同的wifi)  1:命令行运行 ipconfig  2:   ... 192.168.1.101:8080 , 手机访问后者即可  再通过http://cli.im/生成二维码(一个生成二维码...
  • 项目初始化 使用 Vue CLI 创建项目 如果你还没有安装 VueCLI,请执行下面的命令安装或是升级: npm install --global @vue/cli
  • 记录:Vue移动端项目优化过程

    千次阅读 多人点赞 2020-03-13 18:35:08
    项目技术栈:基于Vue全家桶做的一个移动端类似WebApp的项目,使用到的第三方库有:mint-ui, echarts,momentjs。 项目痛点:白屏时间过长 一、白屏时间过长分析 通过访问线上环境,结合Chrome devtool中N...
  • 另外也可以尝试vue-page-stack,两个项目都能实现我们需要的效果,vue-page-stack借鉴了vue-navigation,也实现了更多的功能,并且最近也一直在更新。 PS: 这里的动画效果引用自animate.scss; 底部导航栏 之前我们...
  • 文章目录前言使用navigator.userAgent进行判断,获取访问设备是什么一、navigator是什么?属于Browser 对象:Navigator 对象包含有关浏览器的信息。二、使用步骤1.在methods中使用2. 在你需要使用的地方引入总结...
  • vue3.0 + ts 项目实现pc端和移动端的适配 写在前面:最近写前端项目需要做移动设配和PC端的适配,移动端适配包括了平板和各式各样的手机。之前都是直接使用自己做的rem换算单位来进行适配,但是,现在发现没法适配...
  • 有时我们在开发Web项目的时候会遇到需要适应手机的需求,这时我们可以通过ip地址来连接手机和电脑,在手机上实时预览。 首先查看本电脑的ipv4地址 Window+R,输入cmd回车,调出命令行工具 输入ipconfig命令,即可...
  • 方案:当客户打开项目时,判断设备是移动端还是pc端。根据不同设备给vuex中的变量赋值,首页根据状态值条件判断显示页面哪一部分。(当然也可以设置两个站点,一个m.xxxx.com, 一个www.xxxx.com 前端依据浏览器是...
  • 统一处理页面访问权限 在路由meta字段中配置 需要登录的子段 页面在跳转时 在全局钩子根据该字段进行判断 没有登录next(false) 弹窗 扩展 小程序购物车没有登录会显示 未登录的页面 点击按钮跳转登录页 获取手机号...
  • Vue项目打包成移动端APP

    万次阅读 多人点赞 2019-09-25 20:50:14
    Vue项目打包成移动端APP 需要准备的工具:Hbuilder 目录 Vue项目打包成移动端APP 首先打包vue到dist目录 然后再Hbuilder中打开dist目录 然后将dist包含的 web项目 转换为 移动 APP项目 前几步配置完成后,就...
  • http://192.168.0.104:8080/#/ 访问我的vue项目,浏览器可以打开,手机无法打开,在同一局域网段下。1.设置config文件下的index.js文件: dev: { // Paths assetsSubDirectory: 'static', assetsPublicPath: '/...
  • vue开发移动端环境配置The use of mobile devices has increased considerably in the past decade. It has been over two years since mobile browsing took over desktop. The usability of mobile devices has ...
  • vue项目 在iOS内嵌中运行时, 返回上一页 页面上半部分出现空白!但点击或者滑动,页面就回复正常了。 原因: 由于返回后无法将 body 的高度拉开,使得遮住,触发轻点击,方可消除遮罩针对此问题。 html、body都是...
  • 项目使用vue cli-2搭建了一个简单移动端项目框架,包括loader、plugin、flexible布局、UI框架安装,记录一下。 > A Vue.js project ## Build Setup ``` bash # install dependencies npm i...
  • vue电商项目前端原型

    2018-08-01 14:39:29
    项目其他依赖都弄好了,下载下来后进入项目目录下,输入npm run dev回车后就启动项目了,页面访问http://localhost:8080
  • 和之前项目一样,这里我们还是使用 axios 作为我们项目中的请求库,为了方便使用,我们把它封装为一个请求模块,在需要的时候直接加载即可。 1、安装 axios npm i axios 2、创建 src/utils/request.js /** * ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 7,392
精华内容 2,956
关键字:

移动端访问vue项目

vue 订阅