组件拆分
项目是基于组件化开发的思想,可以看做是在App.vue这个大组件内编写以下小组件:包括头部,中间导航区,以及最底下的内容区,使用vue-router来控制中间导航区(商品,商家,评价)页面的跳转。
1.先引入reset.css(文件路径:项目 -> static -> css)将标签默认样式进行设置,除了文件默认设置外,还会自定义一些标签样式,以保证后期项目更加美观的在手机上进行展现。文件下载地址:http://cssreset.com。
reset.css文件中自定义部分:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /* custom */ 2 a { 3 color: #7e8c8d; 4 text-decoration: none; 5 -webkit-backface-visibility: hidden; 6 } 7 8 li { 9 list-style: none; 10 } 11 12 ::-webkit-scrollbar { 13 width: 5px; 14 height: 5px; 15 } 16 17 ::-webkit-scrollbar-track-piece { 18 background-color: rgba(0, 0, 0, 0.2); 19 -webkit-border-radius: 6px; 20 } 21 22 ::-webkit-scrollbar-thumb:vertical { 23 height: 5px; 24 background-color: rgba(125, 125, 125, 0.7); 25 -webkit-border-radius: 6px; 26 } 27 28 ::-webkit-scrollbar-thumb:horizontal { 29 width: 5px; 30 background-color: rgba(125, 125, 125, 0.7); 31 -webkit-border-radius: 6px; 32 } 33 34 html, body { 35 width: 100%; 36 } 37 38 body { 39 -webkit-text-size-adjust: none; 40 -webkit-tap-highlight-color: rgba(0, 0, 0, 0); 41 }
PS:css样式书写小技巧
像display、position这些会影响布局的样式写在前边
像height、weight这些会触发reload重绘的不可继承的样式写在中间
像字体、颜色等会触发reload重绘的可继承的样式写在最后
2.在 index.html 文件中引入 reset.css 文件,并进行理想视口的定义(宽度,初始缩放比,最大/小缩放比,是否允许缩放):
<meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,minimum-scale=1.0,user-scalable=no"> <link rel="stylesheet" href="static/css/reset.css">
3.eslint.js 文件:
例如:遇到关于分号的错误,因为默认 eslint 是默认不要分号的,如果要加分号,需要在 rules 中添加规则,详细配置规则可以在控制台报错中显示的网址进行查看:
'semi': ['error', 'always'], 'indent': 0, 'space-before-function-paren': 0
4.main.js 文件:/* eslint-disable no-new */
js 中 new 一个对象后是需要赋值给某个值,但是 vue 实例化不需要,所以通过这条规则告诉 eslint 跳过对下面一行代码的规则校验,这样才不会报错。
5.vue模板设置:webstrome主菜单File -> setting -> Editor -> File and Code Templates -> 右侧‘添加 -> 填写Name,Extension,以及代码区(如下)。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 <template> 2 3 </template> 4 5 <script> 6 7 </script> 8 9 <style> 10 11 </style>
6.头部区块代码:在 src -> components -> header -> header.vue中添加代码,并将组件导出;在App.vue文件中引入 header 组件并注册,然后才可以使用header组件。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 <template> 2 <div class="header"> 3 I am header 4 </div> 5 </template> 6 7 <script type="text/ecmascript-6"> 8 export default {}; 9 </script> 10 11 <style lang="stylus" rel="stylesheet/stylus"> 12 13 </style>
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 <div id="app"> 2 <v-header></v-header> 3 <div class="tab"> 4 I am tab 5 </div> 6 <div class="content"> 7 I am content 8 </div> 9 </div>
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 // 引用 2 import header from './components/header/header.vue'; 3 // 注册 4 export default { 5 components: { 6 'v-header': header 7 } 8 };
PS:
1)因为使用的是 stylus 的语法,还需添加 stylus-loader 以及 stylus 组件库,否则会遇到:Module build failed: Error: Cannot find module 'stylus' 这种报错信息。安装语法:npm install stylus-loader --save-dev 以及 npm install stylus --save-dev。
2)在 App.vue 文件中注册 header 组件时,变量名不能为 header ,因为和 html5 标签重复。
3)es6语法,注册组件时可以简写,如 header:header ,简写为 header。
7.导航区代码:
1)在 App.vue中添加导航区代码以及样式。导航区是水平排列,并且是三等分,可以使用移动端经典的布局:flex 布局。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 <div class="tab"> 2 <div class="tab-item">商品</div> 3 <div class="tab-item">评论</div> 4 <div class="tab-item">商家</div> 5 </div>
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #app 2 .tab 3 display flex 4 width 100% 5 height 40px 6 line-height 40px 7 .tab-item 8 flex 1 9 text-align center
PS:
1)记得给 style 标签加上 lang 属性及 rel 属性,否则会因识别不了 stylus 而报错。
2)只需要写标准的 css 代码即可,vue-loader 可以解决 css 兼容性问题。(vue-loader 在 node-modules 文件夹里)
2)安装 vue-router 组件库,通过路由实现页面跳转。
用 Vue.js + vue-router 创建单页应用,是非常简单的。使用 Vue.js ,已经可以通过组合组件来组成应用程序,当要把 vue-router 添加进来时,需要将组件(components)映射到路由(routes),然后告诉 vue-router 在哪里渲染它们。
vue-router官网文档:https://router.vuejs.org/zh-cn/essentials/getting-started.html
2-1)安装命令:npm install vue-router --save。
2-2)App.vue 文件中:使用 router-link 组件来进行商品、评价及商家这三个页面的导航,通过传入 `to` 属性指定链接,最后通过<router-view>将路由匹配到的组件渲染在内容区。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 <div id="app"> 2 <v-header></v-header> 3 <div class="tab"> 4 <div class="tab-item"> 5 <!-- 使用 router-link 组件来导航. --> 6 <!-- 通过传入 `to` 属性指定链接. --> 7 <!-- <router-link> 默认会被渲染成一个 `<a>` 标签 --> 8 <router-link to="/goods">商品</router-link> 9 </div> 10 <div class="tab-item"> 11 <router-link to="/ratings">评论</router-link> 12 </div> 13 <div class="tab-item"> 14 <router-link to="/seller">商家</router-link> 15 </div> 16 </div> 17 <!--内容区--> 18 <div class="content"> 19 <!-- 路由出口 --> 20 <!-- 路由匹配到的组件将渲染在这里 --> 21 <router-view></router-view> 22 </div> 23 </div>
2-3)goods,ratings,seller 组建的创建
在 components 文件夹中新建文件夹 goods,在 goods 文件夹新建文件 goods.vue,同理完成 ratings 和 seller 组件的创建。
以下是 goods.vue 代码内容,ratings.vue 和 seller.vue 同理。
2-4)main.js 文件中:
第一步:把 vue-router 的 export 值赋值给变量 VueRouter 。
第二步:如果使用模块化机制编程,导入Vue和VueRouter,要调用 Vue.use(VueRouter),这样才能成功安装路由功能。
第三步:定义(路由)组件。可以从其他文件 import 进来。(这里涉及文件别名设置知识点,具体可以看下面 PS 内容中第五点)
第四步:定义路由。每个路由应该映射一个组件。 其中"component" 可以是通过 Vue.extend() 创建的组件构造器,或者只是一个组件配置对象。
第五步:创建 router 实例,然后传 `routes` 配置。
第六步:创建和挂载根实例。记得要通过 router 配置参数注入路由,从而让整个应用都有路由功能。
第七步:使用 router.push 设置打开项目后默认显示商品页面。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 // The Vue build version to load with the `import` command 2 // (runtime-only or standalone) has been set in webpack.base.conf with an alias. 3 import Vue from 'vue'; 4 import App from './App'; 5 import VueRouter from 'vue-router'; 6 // 1. 定义(路由)组件.可以从其他文件 import 进来 7 import goods from 'components/goods/goods'; 8 import ratings from 'components/ratings/ratings'; 9 import seller from 'components/seller/seller'; 10 11 // 引入自定义的css样式 12 import 'common/stylus/index.styl'; 13 14 // 0. 如果使用模块化机制编程,導入Vue和VueRouter,要调用 Vue.use(VueRouter) 15 Vue.use(VueRouter); 16 17 Vue.config.productionTip = false; 18 19 // 2. 定义路由 20 // 每个路由应该映射一个组件。 其中"component" 可以是 21 // 通过 Vue.extend() 创建的组件构造器, 22 // 或者,只是一个组件配置对象。 23 // 我们晚点再讨论嵌套路由。 24 const routes = [ 25 {path: '/goods', component: goods}, 26 {path: '/ratings', component: ratings}, 27 {path: '/seller', component: seller} 28 ]; 29 30 // 3. 创建 router 实例,然后传 `routes` 配置 31 // 你还可以传别的配置参数, 不过先这么简单着吧。 32 const router = new VueRouter({ 33 routes, // (缩写)相当于 routes: routes 34 linkActiveClass: 'active' 35 }); 36 37 // 4. 创建和挂载根实例。 38 // 记得要通过 router 配置参数注入路由, 39 // 从而让整个应用都有路由功能 40 /* eslint-disable no-new */ 41 new Vue({ 42 el: '#app', 43 template: '<App/>', 44 components: {App}, 45 router 46 }); 47 // 现在,应用已经启动了! 48 49 // 页面加载自动导航到goods页面 50 router.push('/goods');
2-5)样式设置。
2-5-1)关于 active 的设置
在浏览器调试时,当点击商品、评论或者商家时,可以看到默认添加了一个名为 “router-link-active” 的类,这是vue-router 默认添加的,方便用户进行高亮等配置。可以直接在代码中对这个类名进行样式设置。如果觉得类名太长,可以在 vue-router 实例化进行选项配置,将默认类名改成自己想要的名称,比如将 “router-link-active” 改为 “active”。
2-5-2)1 像素边框的设置
问题背景:因为这是一个 webapp 应用,在手机端调试时会发现 1 像素边框有时候并不精确等于 1 像素,这是和手机的设备像素比(dpr) 有关。
设计思路:先在 .tab 上通过伪类 after,来增加一条下边框的线(写在 mixin.styl 文件中),再通过全局定义的 “border-1px” 这个类,根据手机 dpr 的不同对上边的伪类进行缩放(写在 base.styl 文件中),从而达到1 像素边框的设置。如果要设置上边框,就用伪类 before。
实现步骤:
第一步:在 mixin.styl 文件中完成 “border-1px” 的设置,在App.vue 中通过 @import 引入该文件,然后在 .tab 下使用该类。
第二步:在 base.styl 文件中完成 “border-1px” 的设置,在 index.styl 文件中加载所有样式文件,在main.js 中通过 @import 引入该文件。(这里涉及文件别名设置知识点,具体可以看下面 PS 内容中第五点)
第三步,在App.vue 中使用全局样式 “border-1px”。
注意:icol.styl 文件中有一些关于图标字体原路径的设置,因为路径变化,所以运行时可能会出错找不到文件。遇到这种情况修改一下文件中的路径就可以了。
关于样式设置部分具体顺序可见下图。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 border-1px($color) 2 position relative 3 // 进行伪类设置,一定要设置宽度,因为元素脱离了文档流 4 &:after 5 display block 6 position absolute 7 left 0 8 bottom 0 9 width 100% 10 border-top 1px solid $color 11 content ' '
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 //为什么要写 -webkit :因为这里没有兼容性文件控制,所以要手写兼容性代码 2 //本例需要的是1像素的边框,所以当dpr为1.5时,取0.7倍(1.5*0.7=1.05约等于1)。dpr为2时,取0.5倍(2*0.5=1) 3 @madia (-webkit-min-device-pixel-ratio 1.5) 4 (-min-device-pixel-ratio 1.5) 5 .border-1px 6 &::after 7 -webkit-transform scaleY(0.7) 8 transform scaleY(0.7) 9 10 @madia (-webkit-min-device-pixel-ratio 2) 11 (-min-device-pixel-ratio 2) 12 .border-1px 13 &::after 14 -webkit-transform scaleY(0.5) 15 transform scaleY(0.5)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 <template> 2 <div id="app"> 3 <v-header></v-header> 4 <div class="tab border-1px"> 5 <div class="tab-item"> 6 <!-- 使用 router-link 组件来导航. --> 7 <!-- 通过传入 `to` 属性指定链接. --> 8 <!-- <router-link> 默认会被渲染成一个 `<a>` 标签 --> 9 <router-link to="/goods">商品</router-link> 10 </div> 11 <div class="tab-item"> 12 <router-link to="/ratings">评论</router-link> 13 </div> 14 <div class="tab-item"> 15 <router-link to="/seller">商家</router-link> 16 </div> 17 </div> 18 <!--内容区--> 19 <div class="content"> 20 <!-- 路由出口 --> 21 <!-- 路由匹配到的组件将渲染在这里 --> 22 <router-view></router-view> 23 </div> 24 </div> 25 26 27 </template> 28 29 <script> 30 // 引用 31 import header from 'components/header/header.vue'; 32 // 注册 33 export default { 34 components: { 35 'v-header': header 36 } 37 }; 38 </script> 39 40 <style lang="stylus" rel="stylesheet/stylus"> 41 @import "common/stylus/mixin.styl" 42 #app 43 .tab 44 display flex 45 width 100% 46 height 40px 47 line-height 40px 48 border-1px(rgba(7, 17, 27, 0.1)) 49 .tab-item 50 flex 1 51 text-align center 52 /* &:表示父元素,即.tab-item */ 53 & > a 54 /* 区块点击才能有反应 */ 55 display block 56 font-size 14px 57 color rgb(77, 85, 93) 58 &.active 59 color rgb(240, 20, 20) 60 </style>
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 // The Vue build version to load with the `import` command 2 // (runtime-only or standalone) has been set in webpack.base.conf with an alias. 3 import Vue from 'vue'; 4 import App from './App'; 5 import VueRouter from 'vue-router'; 6 // 1. 定义(路由)组件.可以从其他文件 import 进来 7 import goods from 'components/goods/goods'; 8 import ratings from 'components/ratings/ratings'; 9 import seller from 'components/seller/seller'; 10 11 // 引入自定义的css样式 12 import 'common/stylus/index.styl'; 13 14 // 0. 如果使用模块化机制编程,導入Vue和VueRouter,要调用 Vue.use(VueRouter) 15 Vue.use(VueRouter); 16 17 Vue.config.productionTip = false; 18 19 // 2. 定义路由 20 // 每个路由应该映射一个组件。 其中"component" 可以是 21 // 通过 Vue.extend() 创建的组件构造器, 22 // 或者,只是一个组件配置对象。 23 // 我们晚点再讨论嵌套路由。 24 const routes = [ 25 {path: '/goods', component: goods}, 26 {path: '/ratings', component: ratings}, 27 {path: '/seller', component: seller} 28 ]; 29 30 // 3. 创建 router 实例,然后传 `routes` 配置 31 // 你还可以传别的配置参数, 不过先这么简单着吧。 32 const router = new VueRouter({ 33 routes, // (缩写)相当于 routes: routes 34 linkActiveClass: 'active' 35 }); 36 37 // 4. 创建和挂载根实例。 38 // 记得要通过 router 配置参数注入路由, 39 // 从而让整个应用都有路由功能 40 /* eslint-disable no-new */ 41 new Vue({ 42 el: '#app', 43 template: '<App/>', 44 components: {App}, 45 router 46 }); 47 // 现在,应用已经启动了! 48 49 // 页面加载自动导航到goods页面 50 router.push('/goods');
PS:
1)import VueRouter from 'vue-router' 中,为什么是 vue-router 而不是其他值?
这是根据 node modules -> vue-router ->package.json 文件中 name 的值决定的。
2)关于router-go的改变:https://cn.vuejs.org/v2/guide/migration-vue-router.html#router-go-%E6%94%B9%E5%8F%98 。
3)关于 v-link-active 的替换:https://cn.vuejs.org/v2/guide/migration-vue-router.html#v-link-active-替换。
4)关于 linkActiveClass 的设置:https://router.vuejs.org/zh-cn/api/options.html#linkactiveclass
5)文件别名的设置:在 webpack.base.conf.js 文件中,module.exports -》resolve 下增加代码 'components': '../src/components' ,之后在任何文件中导入 components 文件夹中的文件时,都可以直接写 ‘components/......’,不需要写 ‘../../../components’,就是不需要理会文件所处的相对路径,这在编辑器没有提示功能的时候有很大的用处,不容易出错。配置完需要退出,重新启动下程序,配置才能生效。
6)设备像素比相关知识:http://www.zhangxinxu.com/wordpress/2012/08/window-devicepixelratio/
7)css预处理器(待整理)
8)关于文件:mixin.styl 是一些单独的样式设置;base.styl 是全局的样式设置;index.styl 是将一些需要引用的样式文件加载进来,然后在 main.js 中全局引入样式文件即可,这样代码比较简洁,也不容易出错。
9)关于手机端调试
法一(尝试过没有成功):命令行运行 ipconfig ,找出 ip(假设是192.168.1.111),在手机浏览器页面输入http://192.168.1.111:8080/#/goods 即可看到效果。但是我自己调试过没办法,感兴趣的朋友可以试一下。
法二(成功):在 webpack.dev.conf.js 文件中,找到 devServer 下的 host 的配置进行修改。假设本机 ip 地址是192.168.1.111,则将配置改成 host: '192.168.1.111',保存好后重新跑一下项目就可以了。注意:手机端和电脑必须在同一局域网内,简单地说就是连接同一个 wifi,否则手机端无法看到效果。
10)关于草料二维码:可以复制上述网址 http://192.168.1.111:8080/#/goods 到 草料二维码生成器 生成二维码,然后用手机扫码方式就可直接进入页面,无需手动输入网址,便捷高效。
8.内容区代码:在 App.vue中添加导航区代码以及样式。导航区是水平排列,并且是三等分,可以使用移动端经典的布局:flex 布局。
具体内容后续补充。