美文网首页
项目踩坑系列(一)——vue-cli

项目踩坑系列(一)——vue-cli

作者: LuLuX | 来源:发表于2018-06-01 10:39 被阅读0次

    文章内容大概包括:

    • 选脚手架——vue-cli + 多页面配置
    • 路由去中心化管理
    • 动态标题
    • 打包遇到的问题
    • 一些比较细的小坑

    因为搭建新项目需要,自己搞了一下webpack的东西。之前使用大多是别人搭好的架构,自己新建项目拿来用就好了,没有去自己动手搭过。现在就纯记录一下:

    选脚手架 vue-cli

    需求:一个完整的网站,包括首页、列表页、详情页、播放页等等约30个左右的页面。
    同事推荐用cooking或者vue-cli搭建架构,cooking之前已经有在几个项目中实践过,据描述并不是很好用,配置有些没暴露,有些loader不好配。大概搞了一下之后决定用vue-cli。

    先用npm全局安装vue-cli,还有vue-init,然后执行init来装webpack+vue,初始化项目的时候在命令行有一些选项要配置一下,初始化完成之后进入对应的项目,装一下各种依赖包。装完之后就可以直接npm run dev跑一下啦,美滋滋。详细的步骤可以参考网上的文章,搜一下很多这方面的内容。

    npm install -g @vue/cli
    npm install -g @vue/cli-init
    vue init webpack my-project
    cd my-project
    npm install
    

    vue适合做单页应用,但是领导要求页面要做多页(原公司的项目有多页的,但是代码在原电脑里,所以时下也不方便去找),就网上找了下vue多页的配置。翻了一下之后找了一篇参考:使用Vue-cli搭建多页面应用时对项目结构和配置的调整来实际操作,按照文章来实际操作,没什么问题。但是每个页面都放了一个入口的html文件感觉比较麻烦,于是稍微做了一点调整。在上面文章的基础上修改了build/utils.js文件,把所有页面的入口文件都改成放在外面,不需要每个页面都放置一个入口模板(具体代码看后面)。

    大概调整后的目录结构如下:

    项目整体目录情况 多页,pages目录如下

    几个坑

    配置过程中遇到的主要问题是:
    1、在各种依赖包都安装完了的情况下,如果修改项目名,在跑npm run dev的时候会直接报错。用万能的搜索引擎找了一下原因:npm项目,在安装依赖(node_nodules)时,会记录当前的文件路径,当修改之后就无法正常启动。也是比较蛋疼的,需要删除node_modules目录,再重新npm install。解决方案如下:

    • 删除 node_modules 文件夹(如果修改项目名称,需要在在package.json中修改对应的name)
    • 重新安装依赖 npm install
    • 启动项目 npm run dev

    2、在跑npm install的时候,虽然自己已经设置了科学上网,但是还是会有一些依赖包装不了,所以还是借助淘宝npm镜像cnpm来安装。一行命令就搞定,然后cnpm install美滋滋。虽然网上有人吐槽cnpm有时候会出现漏包的情况,不过我这边跑起来暂时没遇到这种情况。

    npm install -g cnpm --registry=https://registry.npm.taobao.org
    

    3、build打包的问题。在npm run build的时候,因为一开始我的pages目录是这样子的:

    错误示范

    虽然是在pages里面创建了不同命名的文件夹index live user,但是入口html和js文件都是使用index命名的,导致在build的时候生成的dist目录下,只有一个index.html和index.js。然后我就方了,不是说多页面吗,怎么只有一个入口html和js。

    遂重新去看了使用Vue-cli搭建多页面应用时对项目结构和配置的调整 文章下修改的具体配置,找到了build/utils.js文件下的配置,发现了它不是取pages目录下文件夹的名字作为打包完之后入口html和js文件的命名,而是直接取了文件夹内对应文件的名字,并将生成的文件都放在一个目录下,所以就直接覆盖了。

    我这里就很尴尬的三个目录下的文件名都是index。于是对uilts.js文件进行修改,将html和js都修改成:直接提取pages文件夹下的子文件夹的名字,作为 build完之后生成的html和js文件的命名。避免了pages不同目录下的文件名不能重复的情况,同时因为个人习惯,把不同目录下的入口js文件都命名为index.js,方便查看。

    调整后的pages目录如下(这里我删去了之前的index.html,在第三步中说明):

    调整pages目录

    3、pages目录下的每个子目录,都有index.html一个模板入口文件,内容都是一样的,每个目录下放一个相同的文件感觉不太喜欢,遂做一点修改,将其提取出来放在src目录下,所有页面都用同一个模板来生成。

    结合2和3的修改,utils.js某个模块调整如下:

    // yanzi 这块是对js的调整
    //多入口配置
    // 通过glob模块读取pages文件夹下的所有对应文件夹下的js后缀文件,如果该文件存在
    // 那么就作为入口处理
    exports.entries = function() {
        var entryFiles = glob.sync(PAGE_PATH + '/*') //从原来的提取js路径作为filename改为提取目录路径
        var map = {}
        entryFiles.forEach((filePath) => {
            let filename = filePath.split('/').pop() //从目录路径中提取目录名,作为build完js文件的命名
            map[filename] = filePath + '/index.js' //不同页面的入口js统一命名为index.js
        })
        return map
    }
    
    // yanzi 这块是对html的调整
    //多页面输出配置
    // 与上面的多页面入口配置相同,读取pages文件夹下的对应的html后缀文件,然后放入数组中
    exports.htmlPlugin = function() {
        let entryHtml = glob.sync(PAGE_PATH + '/*') //从原来的提取html路径作为filename改为提取目录路径
        let arr = []
        
        entryHtml.forEach((filePath) => {
            let filename =  filePath.split('/').pop(); //从目录路径中提取目录名,作为build完html文件的命名
            let conf = {
                // 模板来源
                // template: filePath,
                template: path.resolve(__dirname, '../index.html'), //将模板来源改为src目录下的index.html
                // 文件名称
                filename: filename + '.html',
                // 页面模板需要加对应的js脚本,如果不加这行则每个页面都会引入所有的js脚本
                chunks: ['manifest', 'vendor', filename],
                inject: true
            }
            if (process.env.NODE_ENV === 'production') {
                conf = merge(conf, {
                    minify: {
                        removeComments: true,
                        collapseWhitespace: true,
                        removeAttributeQuotes: true
                    },
                    chunksSortMode: 'dependency'
                })
            }
            arr.push(new HtmlWebpackPlugin(conf))
        })
        return arr
    }
    

    路由的去中心化管理

    因为页面比较多,搭完项目之后开始搞路由。

    user页面结构

    user是一个页面入口,下面有主页面App.vue以及subPages目录下的多个页面。而subPages目录下的页面,如myVideos文件夹中还有另外的subPages,大概两层路由的情况。

    在编写页面路由的时候,普通的做法是把所有vue文件都在外层的router.js中引入,包括子页面的子页面。这样引入的工作量比较大(引入路径也长),而且集中在外层管理不太方便,生成的路由也会非常大难以管理。

    所以,做了一个去中心化的管理。每个subPages下的页面,都配有自己的routes.js路由,然后在外层的router.js再集中引入。用到的webpack属性是require.context

    首先,在subPages各个目录下先编写好自己子目录的路由,如myVIdeos/routes.js的代码如下:

    const myVideos = () => import('./myVideos.vue')
    const myRecord = () => import('./subPages/myRecord.vue')
    const videoList = () => import('./subPages/videoList.vue')
    
    module.exports = [
      {
        path: '/myVideos',
        component: myVideos,
        name: 'myVideos',
        children: [
          {
            path: 'myRecord',
            name: 'myRecord',
            component: myRecord
          },{
            path: 'videoList',
            component: videoList,
            name: 'videoList'
          }
        ]
      }
    ]
    

    然后再在外层的router.js文件中,遍历subPages目录下的各个routes.js文件,动态加载,集中输出。router.js代码如下:

    // 动态加载各模块路由,./subPages/xx/routes.js
    export default [].concat(...(r => {
      return r.keys().map((key) => {
        return r(key).map(route => {
          return {
            path: route.path,
            name: route.name,
            component: route.component,
            children: route.children || []
          }
        })
      })
    })(require.context('./subPages', true, /^\.(\/\w+)+\/routes\.js$/i)))
    
    

    页面动态标题

    title更新思路:
    1、简单粗暴的document.title='啦啦啦啦'
    2、插件 vue-router-title,直接在meta里面赋title的值。vue-wechat-title 针对微信的标题做一些兼容处理
    3、使用vue的自定义指令:注册一个全局指令,v-title
    举两个栗子:

    // 栗子一
    Vue.directive('title', {
      // 当被绑定的元素插入到 DOM 中时……
      inserted: function (el, binding) {
        document.title = el.innerText
        el.remove()
      }
    })
    <div v-title>这是标题</div>
    //栗子二
    Vue.directive('title', {
      // 当被绑定的元素插入到 DOM 中时……
      inserted: function (el, binding) {
        binding.value
      }
    })
    <div v-title="这是标题"></div>
    

    4、使用slot和组件的生命周期:写一个title组价来更新document.title,写起来更像在页面<title>里面写

    参考:
    vue2.0 下对网页标题(document.title)更新的一种实现思路
    vue-router-title
    vue-wechat-title

    打包遇到的问题

    1、打完包(npm run build)在预览的时候,样式和开发模式下有一些出入,对比发现css缺少了-webkit-box-orient: vertical属性。参考:https://segmentfault.com/q/1010000009360389

    因为项目中用到这个属性的地方可能比较多,懒得一个一个去修改/*! autoprefixer: off *//* autoprefixer: on */,所以采取的方案是直接在webpack.prod.conf.js中关闭了OptimizeCSSPlugin~

    一些比较细的小坑

    1、因为后端的数据库没有对emoji做支持,临时将数据库的编码改成utf8mb4,时间上也来不及。所以暂时先由前端这边做处理,把用户输入的emoji表情过滤掉。用个正则/(\ud83c[\udf00-\udfff])|(\ud83d[\udc00-\ude4f])|(\ud83d[\ude80-\udeff])/匹配,提示用户“不支持表情”。【好像在前司也处理过这个问题。。】

    2、<input type="number">,因为e也是一个数字,但是对于我们简单的输入100以内整数的操作,不需要e,所以增加个处理,也是正则匹配只能输入数字,如下:

    onkeypress='return( /[\d]/.test(String.fromCharCode(event.keyCode)))'
    

    对于用户的输入法设置为英文的时候,这个处理是ok的。但是在用户的输入法设置为中文的时候,输入dede,再按个shift键把中文转为英文,输入框的内容会变成ee。所以这种处理还是存在bug的。

    输入法设置为英文 输入法设置为中文 中文输入dede 变为ee

    3、使用<input type="file">上传文件,一般是监听change事件来做处理,但是如果前后添加的是同一文件,那么change事件不会被触发。解决的思路是在文件上传完之后清空前面的值。

    推荐汇总:

    相关文章

      网友评论

          本文标题:项目踩坑系列(一)——vue-cli

          本文链接:https://www.haomeiwen.com/subject/uedhsftx.html