记:使用vue全家桶 + vux组件库 打包成 dcloud 5+ app 开发过程中遇到的问题
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里面,build的assetsPublicPath路径为” /” 是绝对路径,把它改为相对路径 “./” 就可以了。然后重新打包
项目中,不管是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); }
(1) 用了vuex存放了当前网络状态。一般把断网的显示内容封装成一个组件,然后在这个组件里写入上面的内容。
(2) 然后在 APP.vue 中引入 组件。然后再根据 vuex中网络的状态,决定是否显示断网组件。