记:使用vue全家桶 + vux组件库 打包成 dcloud 5+ app 开发过程中遇到的问题

纵饮孤独 提交于 2019-11-28 15:52:26

vue-cli 版本:2.9.6  

webpack 版本:3.6.0

 

1. vue-cli 安装好之后,不是自动打开默认浏览器

在 config文件夹 ---> dev选项中,有个 autoOpenBrowser 。把它置为 true 即可。

 

2. 使用less(或者sass)全局变量

起因: 因为想定义一些常用的工具样式。类似:超过一行隐藏字体并用省略号显示,清除浮动,主题颜色等。就考虑不用每个页面都引入,能直接使用定义的工具样式。

  第一步: 安装  npm install sass-resources-loader --save-dev 对的,你没有看错,要使用less全局变量,就要安装 sass-resources-loader 这个包

  第二步: 配置:找到build文件夹下面的utils.js 找到  exports.cssLoaders  中的最下面的 return {},然后在less配置项中配置以下代码

配置前:

return {
    css: generateLoaders(),
    postcss: generateLoaders(),
    less: generateLoaders('less'),
    sass: generateLoaders('sass', { indentedSyntax: true }),
    scss: generateLoaders('sass'),
    stylus: generateLoaders('stylus'),
    styl: generateLoaders('stylus')
} 

配置后:

return {
    css: generateLoaders(),
    postcss: generateLoaders(),
    less: generateLoaders('less').concat({ 
      loader: 'sass-resources-loader', 
      options: { 
          resources: path.resolve(__dirname, '../src/assets/css/public.less') 
      } 
    }),
    sass: generateLoaders('sass', { indentedSyntax: true }),
    scss: generateLoaders('sass'),
    stylus: generateLoaders('stylus'),
    styl: generateLoaders('stylus')}

(1) 配置的 less文件路径就是你自己的 工具样式 less 文件路径。用相对路径

(2) 如果是 想要使用 sass的 工具样式。只需要在 同样配置 sass 的地方,后面添加 concat 即可。内容也是一样,除了工具样式 sass文件路径名。

 

3.  vux组件库安装后报错:you may need an appropriate loader to handle this file type

即  三点 ...  这种没有用 适当的 loader 来处理。这样的错误,是因为使用了vux 2x版本,所以先要安装 vux-loader 

  第一步:安装:  npm install vux-loader --save-dev 

  第二步:配置:在 build ---> webpack.base.conf.js  里修改。原来的 module.exports  使用 let originalConfig = xxx 来替换 。配置好后重启项目就能使用了

修改前: 

...
module.exports = { ... }

修改后: 

...
let originalConfig  = { ... }
//下面的代码放到最底部
const vuxLoader = require('vux-loader')

module.exports = vuxLoader.merge(originalConfig , {
    plugins: ['vux-ui']
})

 

4.  import vux 组件,导致 栈溢出: Maximum call stack size exceeded

产生这个的原因是: 引入的组件名 和当前 .vue文件中  export default 的name 值 相同,而且是 不区分大小写的相同

修改方法: 把 name 值改成其它不相同的即可。

 

5. 修改vux 组件库某些组件的样式。

第一种方法: 如果是修改组件中,提供的 样式变量 这种官方提供的样式修改。那么,可以使用下面的这种办法:

  第一步: 在上面第三个安装报错问题处,修改成以下代码:

...
let originalConfig  = { ... }


//下面的代码放到最底部
const vuxLoader = require('vux-loader')

module.exports = vuxLoader.merge(originalConfig , {
    plugins: ['vux-ui', 'progress-bar', 'duplicate-style', {name: 'less-theme', path: 'src/style/vux_theme.less'}]
})

  第二步: src下新建style文件夹,再新建vux_theme.less文件,然后里面加入需要修改的样式变量比如: @tabbar-text-active-color: #09BB07; 

  第三步:重启项目即可。如果把样式放到自己的 less 文件中,我发现还是没起作用。所以还是乖乖的按照上面的步骤来弄。

第二种方法: 也是个人推荐的一种方式

  就是:在 style 中不使用 scoped 属性。那么这样当前页面的 样式就相当于全部公用了。为了防止样式污染,那么就需要在当前 .vue 组件中,最外层 div 加一个class 或者 id 值。这样利用优先级去修改样式即可。

第三种方式: 深度选择器 /deep/ <<<

  大家都知道:如果使用了scoped属性,那么会在组件外层加上一个data-v的标识,导致修改不了组件内部的样式,除非使用 /deep/ 或者 <<< 深度选择器。但是深度选择器不支持less,但是可以转换一种方法来写就可以支持:

@deep: ~'>>>'; 
.parent-custom { 
@{deep} .child-title { 
        font-size:20px;
        color: red; 
    } 
}

(1) 注意 深度选择器 >>>  前面一定要加上  波浪符号 ~  。这样表示不编译后面的内容,全部以字符串输出

(2) 这种深度选择器,也可以用于修改 v-html 的内容中的样式。

 

6.  使用 px2rem 之后,不编译第三方UI组件库的px单位的方法:

  第一步: 不要安装 px2rem-loader 。需要安装 npm i postcss-px2rem-exclude –save  

  第二步: vue-cli搭建的环境中,有个.postcssrc.js文件,然后在这个文件修改成如下的代码:

module.exports = {
  "plugins": {
    "postcss-import": {},
    "postcss-url": {},
    // to edit target browsers: use "browserslist" field in package.json
    "autoprefixer": {},  // 需要添加的内容
    'postcss-px2rem-exclude': {
      remUnit: 75,
      exclude: /node_modules|folder_name/i  // 忽略node_modules目录下的文件
    }
  }
}

(1) 因为第三方 ui 库的meta 都是   <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">  ,使用 flexible 之后会自动添加 meta,当meta 缩放是0.5的时候,就会缩小。

(2) remUnit 在这里要根据 lib-flexible 的规则来配制,如果设计稿是750px的,用75就刚刚好。

(3) 如果不想 让 px 编译成 rem 。可以在这个样式后,加上/*no*/ 。例如  border:1px solid #ccc;/*no*/  

(4) 注意,px2rem 插件 不能让 行内样式 和 js动态加入的 px单位转变成 rem。 如果实在要让 js 中的px单位转换成 rem,那么,可以用 设计稿中的 大小 来除以 remUnit 的大小,得到的就是 正常的 rem值。比如,我 750px 设计图上是 200的宽度,那么 rem 就是 200 / 75 = 2.666666。最后再拼接'rem'成字符串即可。

在第二步中还有的说法是:在 package.json 中配置如下代码:

"postcss": {
    "plugins": {
        "autoprefixer": {},
        "postcss-px2rem-exclude": {
            "remUnit": 75,
            "exclude": "/node_modules|folder_name/i"
        }
    }
}    

不过,配置过后并未起作用。所以建议还是用前面的方法

 

7.  使用 lib-flexible 之后,在index.html中不要添加缩放的那个meta,插件会自动添加。否则会出现将 “将根据已有的meta标签来设置缩放比例” 的警告。

 

8.  dcloud 5+ app 之分享功能。分享总共分三步(记住要先在 manifest.json 文件中的SDK配置中,把申请的分享的各种 appid、appkey等内容填写好):

  第一步:是获取分享服务列表,即 获取 qq,新浪微博,微信。这三个分享服务

  第二步:判断该服务是否授权。如果授权就进入分享的函数,否则去获取授权

  第三步:在授权成功的情况下,就进行分享。

完整的代码如下:

html 部分
<div v-for="(i,idx) in list" :key="'share_'+idx" @click="judegeAuthorize(list[idx].id)"></div>

js 部分
data:function(){
  let curPath = (location.origin + location.pathname).replace('index.html',''); //因为是打包成app,所以没有网络路径,如果要分享本地图片,就必须用这种方式来拼接本地图片的路径。
  let imgPath = curPath + 'static/image/public/defaultHeader.png'; //分享的图片的路径
  return{    shareLink:'http://www.baidu.com', //分享链接
     list: [
        {
          id: "weixin",
          imgurl: "static/image/public/msg_share_weixin.png",
          title: "微信",
          ex:'WXSceneSession'
        },
        {
          id: "weixinFriends",
          imgurl: "static/image/public/msg_share_friends.png",
          title: "朋友圈",
          ex:'WXSceneTimeline'
        },
        {
          id: "qq",
          imgurl: "static/image/public/msg_share_qq.png",
          title: "QQ"
        },
        {
          id: "sinaweibo",
          imgurl: "static/image/public/msg_share_weibo.png",
          title: "微博"
        }
     ],
     shareOptions:{}, //分享渠道:微信、QQ、新浪微博
      //qq分享的内容选项
      qqOptions:{
        title:'这是QQ分享的标题', //(必填,最长30个字符)
        content:'这是QQ分享的内容', //(可选,最长40个字符)
        thumbs: [imgPath], // 数组类型
        href:this.shareLink
      }, 
      //微信分享的内容选项
      weixinOptions:{
        title:'这是微信好友分享的标题',
        content:'这是微信好友分享的内容',
        thumbs: [imgPath],
        href:this.shareLink,
        extra:{
          scene:"WXSceneSession"
        }          
      },
      // 微信朋友圈分享的内容选项
      weixinFriendsOptions:{
        title:'这是微信朋友圈分享的标题',
        content:'这是微信朋友圈分享的内容',
        thumbs: [imgPath],
        href:this.shareLink,
        extra:{
          scene:"WXSceneTimeline"
        }          
      },
      //新浪微博分享的内容选项
      sinaweiboOptions:{
        content:'这是新浪微博分享的内容',
        href:this.shareLink,
        pictures: [imgPath]
      }
  }  
},
created:function(){
    var that = this;
    if(window.plus){
      // 扩展API加载完毕,现在可以正常调用扩展API
      plus.share.getServices(function(data){
        var shareObj = {};
        for(var i in data){
          shareObj[ data[i].id ] = data[i];
        }
        that.shareOptions = shareObj;
      }, function(){
        plus.nativeUI.toast('获取分享服务列表失败')
      }) 
    } 
},
methods:{
    //处理分享功能
    shareHandler:function(target,config){
      target.send(config, function(msg){
        plus.nativeUI.toast("分享到"+target.description+"成功");
      },function(e){
        console.log(e)
        let msg = e.code == -2 ? '已取消分享' : "分享到"+target.description+"失败";        
        plus.nativeUI.toast(msg);
      });                
    },
    //判断是否授权
    judegeAuthorize:function(id){
      var target = this.shareOptions;
      if(JSON.stringify(target) == '{}'){
        plus.nativeUI.toast("分享组件未准备好,请稍后再试");
        return 
      }
      let config = null;
      switch(id){
        case 'weixin':
          config = this.weixinOptions;
          break;
        case 'weixinFriends':
          config = this.weixinFriendsOptions;
          id = 'weixin';
          break;
        case 'qq':
          config = this.qqOptions;
          break;
        case 'sinaweibo':
          config = this.sinaweiboOptions;
          break;
      }
      var that = this;
      if(target[id].authenticated){
        this.shareHandler(target[id],config);
      }else{
        target[id].authorize( function(){
          plus.nativeUI.toast("授权成功");
          that.shareHandler(target[id],config);
        }, function(e){
          plus.nativeUI.toast("授权失败");
        });
      }      
    }
}

注意事项:

(1) 朋友圈和微信好友的区别就是 扩展内容 extra 里的 scene 不同。

(2) 分享的图片路径,都是数组类型。

(3) 微信的分享,如果是直接返回,并没有分享,但是结果返回也是分享成功。新浪微博获取不了分享是否成功的状态,不知道是否我哪里弄错了。如果qq分享失败,查看参数是否正确,或者图片是否是数组类型。

(4) 可以不用官方的 document.addEventListener('plusready',function(){})  来判断 plus是否准备好。可以直接判断 window.plus 来看plusready是否准备好。

 

9. 引入 mui.js 报错

引入:在 main.js 中导入  import mui from './assets/js/mui.js'  。然后报错了。 may not be accessed on strict mode

产生这样的原因是因为: vue-cli 使用了 babel 把es6 转成es5 ,默认的是使用的严格模式。解决的办法:在 .babelrc 文件中修改

  增加忽略babel编译的文件:在与 plugins 同级的位置添加:  "ignore":["./src/assets/js/mui.min.js"]    后面的就是 mui.min.js 的文件位置 

然后就可以 把 mui 挂在到 Vue 原型上了。

 

10.  设置沉浸式导航栏

if(window.plus){
      console.log('设置状态栏')
      plus.navigator.setStatusBarBackground('#fff');//设置状态栏背景颜色
      plus.navigator.setStatusBarStyle('light');//设置状态栏字体颜色
}

(1) 状态栏字体颜色 只支持 light 和 dark 两个值。

(2) 如果在设置了 keep-alive 的组件页面中,有可能前面的页面会影响当前页面。那么可以在 activated,deactivated 的 方法里面再设置一次。

 

11.  打包成app之后,安卓后退键的处理。有两种方式:

  第一种: 使用 5+ 监听安卓后退按钮。在vue的生命周期中调用

document.addEventListener('plusready', function() {
      var first = null;
      var webview = plus.webview.currentWebview();
      plus.key.addEventListener('backbutton', function() {
        webview.canBack(function(e) {
          if (e.canBack) {
            webview.back(); //这里不建议修改自己跳转的路径
          } else {
            //首次按键,提示‘再按一次退出应用’
            if (!first) {
              first = new Date().getTime(); //获取第一次点击的时间戳
              plus.nativeUI.toast('再按一次退出应用', {
                duration: 'short'
              }); 
              setTimeout(function() {
                first = null;
              }, 1000);
            } else {
              if (new Date().getTime() - first < 1000) {
                //获取第二次点击的时间戳, 两次之差 小于 1000ms 说明1s点击了两次,
                plus.runtime.quit(); //退出应用
              }
            }
          }
        })
      });
    })

(1) 这种不支持用 mui.openWindow 打开的页面的返回。除非 再去判断 是否有 指定ID 打开的webview  plus.webview.getWebviewById('自定义的页面id')  。然后再去关闭指定ID的webview页面。 plus.webview.close('jmyc') 

(2) 这种方式也有点问题,两次的点击时候需要小于1s才能后退。

  第二种: 引入 mui 。在vue的生命周期中调用:

data:{
    openWebView:false, //是否打开了页面
},
created:function(){
  this.$mui.init({
      beforeback: function(){
        if(_this.openWebView){
          //获得列表界面的webview jmyc
          var viewObj = plus.webview.getWebviewById('jmyc');
          //canBack查询窗口是否可退
           viewObj.canBack((event) => {
            var canBack = event.canBack;  
            if(canBack) {//如可退,则返回上一页面
              viewObj.back();
            } else {//如不可退,则退出窗口
              plus.webview.close('jmyc')
              _this.openWebView = false;
            }
          })
          return false;
        }
        return true;
      }
   });  
}

(1) 前提是mui已经在 main.js 中引入,并且挂在到了vue 原型上 

import mui from './assets/js/mui.min.js'
Vue.prototype.$mui = mui

(2) 这种方式的好处就是,不用去判断是否超过一秒。

 

12. 路由的懒加载,实现按需加载。

// 实现路由懒加载
function lazyLoad(filename){
  return () => import(`@/page/${filename}/index.vue`);
}

export default new Router({
  routes: [
    {
      path: '/index',
      name: 'indexToo',
      component: lazyLoad('index'),
      meta: {
        keepAlive: true // 需要被缓存
      },
    {
      path: '*',
      name: 'z_page404',  //404页面 , 必须放在最底部
      component: lazyLoad('z_page404')
    }    
  ]
})

(1) 上面这种写法,是因为 每个 page 都新建了一个 index.vue 文件当做页面,然后文件夹以不同的名称来区分。

 

13.  需要缓存的页面的处理:

第一步: 需要缓存的才使用 keep - alive 

<keep-alive>
    <router-view v-if="$route.meta.keepAlive"></router-view>
</keep-alive>
<router-view v-if="!$route.meta.keepAlive"></router-view>

第二步: 在 router.js中配置 meta 

export default new Router({
  routes: [
    {
      path: '/index',
      name: 'index',
      component: lazyLoad('index'),
      meta: {
        keepAlive: true // 需要被缓存
      }    
  ]
})

如果想某些页面动态来判断是否缓存: 比如,当前页面是 A ,需要缓存,但是从 B 页面返回来之后,A页面需要更新内容就不能缓存。在与 created 生命周期 同级上写。

// 如果路由进入前,是从 B页面返回来的,那么就设置当前 A 页面不缓存,否则缓存
  beforeRouteEnter(to, from, next) {
    if(from.path == '/B'){
      to.meta.keepAlive = false; 
    }else{
      to.meta.keepAlive = true;
    }
    next();
 },

(1) 最后一定要调用 next() 方法。

 

14. 打包成app 之后,图片,css,js等路径不正确

  如果没有修改其它的路径什么的,多半是config文件夹中index.js里面,buildassetsPublicPath路径为” /” 是绝对路径,把它改为相对路径 “./” 就可以了。然后重新打包

  项目中,不管是img标签中的图片,还是 css中的背景图片,都需要使用相对路径

 

15.  如果想更方便的调试 5+ plus的效果:

  建议在 5+ app 的 index.html 添加一个 a标签,href就是当前 项目运行的网络路径。 这样只用在基座上运行一次,然后项目每次修改,都可以看到效果。

  如果是自定义基座,运行了之后,怕看不到控制台的内容,hbuilderX 自带有 webview 调试。运行之后,有个链接可以点击,就可以使用谷歌的 inspect 来调试

  或者 使用 Vconsole 来调试

 

16. 断网的处理

data:{
    networkStatus: false
},
created:function(){  
    let that = this;
    document.addEventListener("netchange", function () { //监听网络变化事件  
      // 延迟二秒获取网络状态
      setTimeout( () => {
        let target = plus.networkinfo.getCurrentType();
        // 0 表示不认识的网络。 1表示未连接网络
        if ( target == 0 || target == 1) {  
          console.log('无网络连接',target)
          that.networkStatus = true;
          that.$store.commit('changeNetworkStatus',true);
        }else{
          that.networkStatus = false;
          that.$store.commit('changeNetworkStatus',false);
        }
      },2000)        
    }, false);  
}

 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!