关于微信小程序的书籍集合

《小程序开发原理与实战》

开发 app

1、不菲的开发成本

2、高昂的推广成本

3、高昂的维系成本

开发小程序

1、计算能力不足

2、灵活性不足

3、有一定的局限性

三种运行载体的快速对比

运行载体 运行环境 功能性 便捷性 交互体验 开发成本 推广难度 消息推送
公众号 H5 简单 无需安装 一般 支持
小程序 微信 轻应用 无需安装 接近原生 App 受限
App 原生系统 丰富 需要安装 最流畅 支持

小程序的生命周期

场景值

利用场景值做数值统计

例如:场景 1129 (微信爬虫访问)可以用来对爬虫做一些优化,方便索引;场景 1038(从另一个小程序返回)可以用来对小程序免密签约的场景进行判断。

可以在 App 的 onLaunch 和 onShow 中 options.scene 或 wx.getLaunchOptionsSync 中获取上述场景值。

小程序生命周期的注册

(1)App() 必须在 app.js 中注册,不可以在 App()函数内或在定义 App() 函数前调用 getApp()方法;

(2)通过 getApp() 方法获取实例后,不可以私自调用生命周期函数。

Page 的 prototype 中还注册了 data 属性、route 属性、setData()方法。

1
2
this.route  //  获取当前所处的页面
getCurrentPage() // 获取当前页面的实例

data 会以 JSON 的形式由逻辑层传至渲染层,所以数据必须可以转换 JSON 的格式:字符串、数字、布尔值、对象、数组。

this.setData() 仅支持可 JSON 化的数据(即字符串、数字、布尔值、对象、数组)。单次设置的数据不能超过 1024 KB,尽量避免一次设置过多的数据。

全局变量使用

1
2
3
4
5
6
7
8
//  app.js
App({
globalData : 1
})

// a.js
var app = getAppp()
app.globalData++

小程序页面栈的表现形式

路由方式 页面栈变化 老页面生命周期变化 新页面生命周期变化
初始化 新页面入栈 onLoad()、onShow()
打开新页面 新页面入栈 onHide() onLoad()、onShow()
页面重定向 当前页面出栈,新页面入栈 onUnload() onLoad()、onShow()
页面返回 页面不断出栈,直到目标返回页 onUnload() onShow()
tab 切换 页面全部出栈,只留下新的 tab 页面 视具体情况而定 视具体情况而定
重加载 页面全部出栈,只留下新的页面 onUnload() onLoad()、onShow()

注意事项

(1)navigateTo、redirectTo 只能打开非 tabBar 页面。

(2)switchTab 只能打开 tabBar 页面,但是不能传递参数。

(3)reLaunch 可以打开任意页面

(4)页面底部的 tabBar 由页面决定,即只要是定义为 tabBar 的页面,底部都有 tabBar。

(5)调用页面路由带的参数可以在目标页面的 onLoad 中获取。

1
2
3
4
5
6
7
8
9
10
//  common.js
function sayHello (name) {
console.log(`Hello ${name}!`)
}
function sayGoodbye (name) {
console.log(`Goodbye ${name}!`)
}

module.exports.sayHello = sayHello
exports.sayGoodbye = sayGoodbye
1
2
3
4
5
6
7
8
9
var common = require('common.js')
Page({
helloMINA: function(){
common.sayHello('MINA')
},
goodbyeMINA: function(){
common.sayGoodbye('MINA')
}
})

注意事项

(1)exports 是 module.exports 的一个引用,在模块里随便更改 exports 的指向会造成未知的错误。推荐开发者采用 module.exports 来暴露模块接口,除非你已经清晰地知道两者之间的关系。

(2)小程序目前不支持直接引入 node_modules ,建议开发者在需要使用 node_modules 时复制相关的代码到小程序的目录中,或者使用小程序支持的 npm 功能。

api 分为事件监听类、同步执行类、异步执行类。

封装 api 接口

1
2
3
4
5
6
7
8
let getImageInfoPromise = new Promise(function(resole,reject){
wx.getImageInfo({
src: '',
success: function(res) {
resolve(res);
}
})
})

组件

scroll-view

在滚动 scroll-view 时会阻止页面回弹,所以在 scroll-view 中 滚动是无法触发 onPullDownRefresh 的。

若要使用下拉刷新,请使用页面的滚动,而不是 scroll-view,这样也能通过点击顶部状态栏回到页面顶部。

WXSS

在特殊机型(例如 iphoneX、iphone 11)中,因为取消了物理按键,会出现手机底部区域被手机底部的小黑条遮挡的情况,此时可以使用 padding-bottom: env(safe-area-inset-bottom)来告诉微信进行自动适配。

1、清除浮动的元素本身不能为浮动元素

2、清除浮动的元素本身必须是块级元素。

相对定位,相对的是原来的位置,但原来所占的空间仍然保留。

实战:商城类项目开发

典型的电商交互闭环

项目目录结构

  • components 目录:主要用来存放一些自定义组件相关的内容
  • images 目录:主要用来存放商城用到的图片信息等
  • libs 目录:主要用来存放商城项目中依赖的第三方库。
  • models 目录:主要用来封装与后台进行交互的 model 操作类。
  • pages 目录:主要用来存放商城要用到的各个具体页面,里面的每个不同的子目录都代表一个独立页面,分别包含与目录同名的 wxml、wxss、js、和 json 文件。
  • utils 目录:主要用来存放商城开发过程中要使用到的各种工具类,避免重复代码。
  • app.js:小程序主逻辑入口。
  • app.json:小程序全局配置文件。
  • app.wxss:小程序公共样式表。

wxss 文件不支持使用本地图片作为资源

双列效果

1
2
3
4
5
6
7
8
9
10
11
12
13
.djbox {
float: left;
margin: 0 0 1%;
width: 49.5%;
box-sizing: border-box;
min-height: 240px;
postion: relative;
background: #fff;
}

.djbox:nth-child(odd){
margin-right: 1%;
}

自定义模板

1
2
3
4
//  cart.wxml
<template name = "cart">
<view class="cart-dialog slideInUp"></view>
</template>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
//  cart.js
// 模板调用页面的 page 对象缓存


// 模板内部方法,主要是需要满足小程序的事件绑定机制
// 事件方法必须绑定在当前 page 对象上

let methods = {
/**
* 显示添加购物车组件
*/
showShoppingCart(){
// 略
},

hideShoppingCart(){
// 略
},
}

module.exports = {
async init(curPage,goodsId, mode = 3){
page = curPage;
page.setData({
shoppingCart: {
buyNum: 1,
}
})
}
}
1
2
3
//  index.wxml
<import src="../../template/cart/cart.wxml"></import>
<template is="cart" data = "{{shoppingCart}}"></template>

《小程序,巧应用:微信小程序开发实战(第2版)》


《微信小程序:开发入门及案例详解》

第 2 章 小程序开发核心

小程序 js 模块化机制遵循 CommonJS 规范,跟 node 遵循的规范一样。

引用

一个 WXML 可以通过 import 或 include 引入其他 WXML 文件,

两种方式都能引入 WXML 文件,区别在于

import 引入 WXML 文件后只接受模板的定义,忽略模板定义之外的所有内容,而且使用过程中有作用域的概念。

include 则是引入文件中除 <template/> 以外的代码直接拷贝到 <include/>位置

整体来说 import 是引入模板定义,include 是引入组件。

1
2
3
4
5
6
7
8
<import src="b.wxml">
<template is="bTemplate" data=""></template> <!-- 使用b.wxml中定义的模板 -->

<view>内容</view> <!-- import 引用时会被忽略 -->
<template name="bTemplate">
<view>b template content</view>
</template>
<template is="bTemplate"/> <!-- import 引用时会被忽略 -->

import 引用有作用域概念,只能直接使用引用的定义模板,而不能使用间接引入的定义模板。

例如:在 a.wxml 中引入 b.wxml,b.wxml 在引入 c.wxml,a 能直接使用 b 中定义的模板,b 能使用 c 中定义的模板,但 a 不能使用 c 中的模板。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<import src="b.wxml">
<template is="bTemplate"/>
<template is="cTemplate"/> <!-- 不能直接用 c.wxml 中的模板 -->

<import src="c.wxml"/>
<view> b content </view> <!-- import时被忽略 -->
<template name="bTemplate">
<template is="cTemplate" />
<view>b template content</view>
</template>

<template is="cTempalte"/> <!-- import 时被忽略 -->
<template name="cTempalte">
<view>c template content</view>
</template>

include 引入会将模板定义标签外的内容(含模板使用标签)直接赋值替换 <include/>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<include  src="b.wxml">
<template is="bTemplate"/> <!-- 不能调用 b.wxml 中的模板 -->
<template is="cTemplate"/> <!-- 不能调用 c.wxml 中的模板 -->

<include src="c.wxml"/>
<view> b content </view> <!-- 不会忽略 -->
<template name="bTemplate">
<template is="cTemplate" /> <!-- 不会调用 c.wxml 中的模板,引用时被忽略 -->
<view>b template content</view>
</template>
<template is="bTempalte" data="{{name}}"/> <!-- 没有被忽略,能正常调用自己文件中的模板 -->
<template name="cTempalte">
<view>c template content</view>
</template>
Page({
data: {
name: '2' / * 能将数据注入到 b.wxml 中 */
}
})

通过对比发现,import 更适合引用模板定义文件,include 更适合引入组件文件。

在开发过程中一定要开启开发者工具中 “ 开启上传代码时样式文件自动不全 ” 功能,这样小程序会自动不全其余一些样式兼容性写法,

保证在不同终端达到统一视觉效果。

模块的使用

通过 exports 暴露接口。

通过 require(path) 引入依赖,path 是需要引入的模块文件的相对路径。

注意:

exports 是 module.exports 的一个引用,因此在模块里面随意更改 exports 的指向会造成未知的错误。所以更推荐开发者采用

module.exports 来暴露模板接口,除非你已经很清晰地知道这两者的关系

小程序目前不支持直接引入 node_modules,开发者需要使用 node_modules 时建议拷贝出相关代码到小程序目录中。

JavaScript 运行环境

微信小程序逻辑代码运行在三端, iOS、Android、开发者工具,这三端是各自不同的三个解析引擎:

在 iOS 上,小程序的 JavaScript 代码是运行在 JavaScriptCore 中。

在 Android 上,小程序的 JavaScript 代码是通过 X5 内核来解析。

第 6 章案例分析:豆瓣电影

开放平台:淘宝开放平台、腾讯开发者平台、高德 API、百度 API 等。

主要使用正在热映、即将上映、电影条目信息这 3 个 API 接口:

正在热映 API:https://api.douban.com/v2/movie/in_theaters? city=北京
即将上映 API: https://api.douban.com/v2/movie/in_theaters?start=0&count=20
电影条目信息 API:https://api.douban.com/v2/movie/subject/1324043

由于豆瓣 API 不能支持非浏览器客户端发起的请求,非浏览器客户端需要下载对应的 SDK 来请求 API,
所以小程序在微信中打开时不能直接调用豆瓣 API 接口,所以我们利用自己的服务器做了一次请求跳转。

技术架构

借用服务端三层架构模式,一个项目可分为表现层、业务逻辑层、数据访问层。

lib:放置一些最底层、第三方库,如 jquery、seajs、qrcode、echarts 等,通常 lib 层是全公司共用一套。豆瓣电影中由于没有第三方库文件,或者说小程序框架本身就是一个第三方库,所以没有 lib 层。

common:放置和项目相关的一些公共代码,如转码、工具包、公共样式设置等。根据业务需要可以将 widgets 和 common 合并为一个目录。

service:业务逻辑层,按业务类型整合相关的方法,向上暴露需要的接口方法,比如 a 页面需要调用登录和列表信息,b 页面需要调用登录和详情信息,那么这两个页面中的登录接口就会被调用两次,这时我们便可以将这种和业务紧密相关的接口调用方法封装起来,作为一个服务暴露给上层应用。业务逻辑层能降低表现层对业务的关注,让更多精力关注在页面渲染、页面交互等功能上。

widgets:一些通用的带 UI 的小组件,如 pop、header、购物车图标等等,它和 common 层有些类似,但是相对 common 层,widgets 具备 UI 界面和一个闭环的交互功能。由于业务问题,在豆瓣电影实例中没有 widgets 目录。

pages:表现层,一般表现层中一个文件夹对应一个页面所涉及的所有资源。

这种分层方式是前端项目常见的架构分层方式,适用于任何原则,同时按照组件化原则我们通常会把一个组件、包、页面所涉及的所有资源放置在一个目录中。

basestlye:一些项目公共的基本样式,被 app.wxss 引入,当前项目中没有公共样式,内容为空。

utils:基础工具包,项目中我们把系统 toast 进行了一层代理,封装到对应方法中,如果以后需要改变 toast 行为,我们便可以直接修改代码,而不用在每个调用页面进行修改。

detail:详情页相关资源。
home:首页相关资源。
douban:请求豆瓣接口的相关逻辑代码。

公共模块开发

在 JavaScript 整体编码中一般 handle 是需要对外暴露的公共方法的句柄,写在模块前面,
_fn 是私有方法,写在后面,这样便于他人阅读代码。


《 微信小程序项目开发实战:用WePY、mpvue、Taro打造高效的小程序》

第 11 章实战:使用 Taro 实现星座测试小程序

Taro 是一套遵循 React 语法规范的多端开发解决方案。

Taro 遵循 React 语法规范,它采用与 React 一致的组件化思想,组件生命周期与 React 保持一致,同时支持 JSX 语法,让代码具有更丰富的表现力,使用 Taro 进行开发可以获得和 React 一致的开发体验。

采用 Taro 开发小程序具有以下的优秀特效:

  • 支持使用 npm/yarn 安装管理第三方依赖。
  • 支持使用 ES7/ES8 甚至更新的 ES 规范,一切都可自行配置。
  • 支持使用 CSS 预编译器,例如 Sass 等。
  • 支持使用 Redux 进行状态管理。
  • 支持使用 Mobx 进行状态管理。
  • 小程序 API 优化、异步 API Promise 化等。

Taro 项目的编写采用 React 语法,所以引入本地静态文件应当使用 import 语句

1
2
import bg from '../../public/bg.jpeg'
import './index.less'

Taro 提供的 Image 组件可完成图片内容的显示

1
<Image src={bg} mode="widthFix" className="bg" />

在组件中绑定的监听事件,需要使用.bind 的方式声明事件。

1
onClick={this.onTap.bind(this,item)}

该方法要执行的事件,需要在组件中声明。

1
2
3
4
5
onTap(name){
Taro.navigateTo({
url:''
})
}

URL 地址传参

1
2
3
Taro.navigateTo({
url: '/pages/page/path/name?id=2&type=test'
})

在 componentWillMount 方法里通过 this.$router.params 获取到传入的参数。

1
2
3
componentWillMount () {
console.log(this.$router.params) // 输出 { id: 2, type: 'test' }
}

发起网络请求,支持 Promise 化使用。
Taro.request()

赋值
setState()

项目编译与生成
Taro 并不是所有的内容都被各类小程序所支持的,不同小程序也因为平台的不同对部分 API 的支持不同,所以开发者为了开发全平台支持的小程序,需要尽可能地使用 Taro 提供的通用组件。

编译为微信小程序

1
npm run build:weap

编译为百度小程序

1
npm run build:swan

需要下载安装百度开发者工具

编译为支付宝小程序

1
npm run build:alipay

需要下载安装支付宝开发者工具

编译为字节跳动小程序

1
npm run build:tt

编译为 H5

1
npm run build:h5

《微信小程序开发入门与实践》

整本书围绕着 Orange Can 项目展开,讲解 API、组件用法、技巧以及常见的误区说明并附带一些小程序开发的经验。

Orange Can 项目分为

  • 文章阅读
    • 文章列表
    • 文章详情
    • 评论
  • 电影资讯
  • 设置

通过 Orange Can 你将学会

  • swiper组件的裁剪模式
  • image组件的裁剪模式
  • 缓存的使用技巧
  • 列表渲染
  • 数据绑定
  • 模板
  • 对小程序页面的生命周期有大致了解
  • 如何调用服务器数据
  • template 模板的使用技巧
  • 获取硬件设备信息、罗盘与重力感应的应用
  • 扫描二维码
  • 用户登录、用户信息校验、解析用户加密数据、获取用户openId、发送模板消息、微信支付
  • 音乐播放、录音、分享等

通过 Orange Can 你将有能力构建出一个内容型的小程序应用。
本书还提供部分服务器的PHP代码,主要供用户登录、校验、解析加密数据、模板消息、微信支付等功能调用。

前沿

作者的平台信息
微信公众号:小楼昨夜又秋风
知乎专栏:小楼昨夜又秋风
知乎ID:七月在夏天

本书源码
开发者社区

第1章 微信小程序简介

1.1 什么是微信小程序

小程序的特性

  • 小程序没有“官方的”应用市场
  • 小程序不能被关注,小程序没有粉丝这个概念。
  • 小程序没有群发消息和主动推送消息的能力。
  • 微信中没有明显的小程序入口,但是发现有一个【小程序】栏,它会记录用户曾经用过的历史小程序

获取和使用小程序的途径

  • 扫描二维码、有限能力的搜索、群聊分享、小程序栏
  • 长按进入小程序
  • 小程序名称不能和公众号相同
  • 有一定的分享能力,可以分享给朋友和群聊,但不能分享到朋友圈
  • 同一个公司的公众号和小程序可以相互发现。公众号里可以看到该公司的小程序,同时,小程序里也可以看到该公司的公众号。

1.2 什么类型的应用适合用小程序开发

“简单的”、“低频的”、“对性能要求不高的”应用适合用小程序来开发。
小程序特别适合做线下的场景化应用,官方的一切措施都是为了将小程序导向线下。

1.3 小程序与原生App(iOS、Android)的优劣对比

Web App
相对于其他两种App,Web App具有开发简单、高效,更新灵活、跨平台,大量的网页应用稍作调整即可放在移动端运行。但缺点与优点并存,Web App性能、体验极差(对,是极差),无法使用照相机、系统通知、本地缓存等原生特性。

Native App
Native App具有性能、体验非常良好,组件支持完善、接口丰富等特点。但Native App最大的缺点在于,不能跨平台,有多少个平台就要开发多少个版本,现在主要有iOS和Android两个主流平台。

Hybrid App
Hybrid App具有接近于Native App的体验、开发效率高、跨平台等特性。

相比于Native App,小程序具有Hybrid App的一些优势:
跨平台(对于iOS和Android两个平台只需要开发一套程序)。
具备接近于Native App的体验(注意只是接近)。
对原生组件有访问能力。
具备缓存能力。
上手容易,开发逻辑较为简单。

小程序独有的特点

  • 小程序在设计时就做了很多约定式的规范:比如简单的文件结构、默认的文件命名、内置好的Tab栏与导航栏等,这让小程序的初学者更容易上手和理解。
  • 开发环境很干净,你无须安装任何除开发工具外的其他软件。当然现在这个工具简陋的可怕,很多常见的IDE功能都不具备,但相比于其他Hybrid App的环境要求,小程序这点真的很棒。
  • 发布和部署流程非常简单,几乎是“傻瓜式”,点击几下就可以将应用发布到腾讯云。
  • 小程序之所以在公布后引起了互联网圈儿和开发者们极高的关注度,原因并不在技术上,无数开发者、创业者看中的是微信天然的关系链与获客能力。这也是小程序最大的优势。

小程序独有的缺点:

  • 小程序为了简化复杂性,做了一些UI上的设计规范,确实方便了很多对UI要求不高的应用。但这也限制了那些对UI要求极高的产品发挥。
  • 小程序很遗憾地不支持现有的HTML DOM结构,而是自己给出了一系列的组件,造就了一个封闭的开发环境,这直接导致了现有的经典JavaScript框架、类库都无法使用。小程序现在的生态几乎是荒芜一片,等待着开发者们去耕耘(挑战与机遇并存,正因为没有,才有机会)。如果你想用小程序实现一组图形来展现股票或者天气的曲线,目前来看,相当烦琐。你无法使用经典的echart或者highchart,你只能自己用Canvas来一点点地绘制。
  • 截止到笔者编写本书时,小程序还不支持WebView,这是相当头疼的一个问题。
  • 小程序只实现了模板化并没有实现自定义组件。通常希望把这个组件的标签、样式以及业务逻辑打包在一起,然后可以放在项目中多个地方使用。外部客户端调用组件时,只需要传入组件所需要的参数,由组件自己来完成数据获取、转化、绑定并和UI层通信等操作。

小程序和现在主流App的优劣对比

1.4 小程序会淘汰原生App吗

不会。小程序的定位也非常明确——做低频和业务逻辑不复杂的应用,原生App与小程序之间更多地将是一种互补的关系,绝对谈不上取代。

1.5 Web前端的未来

从信息的角度讲,移动端主要负责信息的输出,而PC端主要负责信息的输入。

不可或缺的 Web前端开发者:

  • 混合式App是现在的主流App,一个App很难只用Objective-C或者Java来开发,必然会有Web技术介入。
  • 大多数移动端应用也都有一个对应的Web网站。
  • 现在的公司做营销和推广都离不开微信,无论是H5页面还是做微信服务号、企业号都是纯粹的Web技术。

1.6 Web前端开发者与小程序

微信小程序更多的是App或者Web网页的另一个流量入口,但绝不会替代原生App或者Web网页(至少很长一段时间内是这样,未来小程序怎么发展还有待观察)。正如我们描述的微信对现在公司营销的重要性,小程序也将成为Web前端开发者应该掌握的一门技术。

1.7 MINA框架与微信小程序

信小程序并不是一项技术或者一个框架,微信小程序是一个生态,与之对应的应该是iOS生态和Android生态,其中微信小程序又与iOS生态极为相似,它们都非常封闭,而且审核非常严格(微信小程序的审核比苹果还要严格)。而MINA是小程序的一个框架,它提供了小程序运行所需要的接口、模型和机制。

1.8 微信小程序beta测试版

测试版本的概念非常重要,可以让开发者预先知道下个版本的更新内容,建议开发者应该第一时间使用测试版本测试一下自己的小程序。

第2章 小程序环境搭建与开发工具介绍

2.1 微信Web开发者工具下载及安装

微信Web开发者工具

AppID代表微信小程序的ID号,必须拥有微信小程序账号才可以申请这个ID号。

2.3.1 编辑选项卡

wifi 一栏,可以选择不同的网络环境。

2.3.2 调试选项卡

【区域1】模拟器同编辑选项卡功能一样。
【区域2】展示了小程序经编译后生成的文件和文件结构。
【区域3】是编译后的文件内容查看区,可以在这个区域的文件里设置调试断点。
【区域4】调试功能区,可以在这里查看变量状态与数值、断点设置情况、变量作用域等。这个区域和Chrome浏览器里的调试工具几乎一样,调试的方法和快捷键也是相同的。
【区域5】信息输出区域,这个区域会显示程序运行的错误信息、警告信息以及用户自己打印的相关信息(通过console.log()输出的信息)。

如何在小程序中调试?
可以在【区域3】中点击每行代码前的行号设置断点。当代码运行到断点处后,将停止。常用快捷键有【F10】单步执行,【F11】进入方法,【F8】继续运行到下一个断点。更多快捷键可自行在【区域4】中查看。

在整个调试选项卡中,最重要的部分还是【区域2】和【区域3】顶部的6个功能模块儿。
调试选项卡的6个功能Panel
【Sources】Sources Panel是默认的panel,用于显示当前项目的脚本文件。
Sources Panel
【Console】Console Panel用于调试和输出信息,开发者也可以在这里输入代码。
panel在【Sources】模块中同样存在。
当点击【Console】后,consolePanel将变大,使开发者可以浏览到更多信息。
Console panel
【Network】Network Panel主要用于观察和显示网络连接的相关情况。这里的NetworkPanel和Chrome浏览器里的Network Panel几乎一样。
Network Panel
【Storage】Storage Panel用于显示当前项目的数据缓存情况。
【AppData】AppData Panel用于显示项目中被激活的所有页面的数据情况,这些数据主要是用来做数据绑定。
【Wxml】Wxml Panel是非常重要的一个功能模块,这个模块类似于Chrome调试工具下的Elements模块,主要用于调试Wxml标签和相关CSS样式,调试方法同Chrome一样。如果你是一个前端新手,那么建议好好地摸索一下这个功能模块的调试技巧,绝大多数和样式、标签相关的问题,都需要依靠这个Panel来调试。
Wxml Panel

2.3.3 项目选项卡

项目选项卡主要用来管理和设置项目。
项目选项卡

项目选项卡主要有3大功能:显示当前项目细节、预览及上传项目以及项目配置。

显示项目细节
顶部显示了项目名称和App ID。如果我们新建项目时选择的是“无AppID”,则这里将会固定地显示一个“touristappid”;如果你设置了项目的AppID,则这里会显示项目的AppID。

预览及上传项目
预览按钮可以实现在真机上运行小程序,但前提条件是你必须有一个微信小程序账号。

项目配置
开启ES6转ES5
小程序支持使用ES6来编写代码。如果使用ES6来编写代码,框架会默认使用babel将开发者的代码转换为ES5代码(这样做的主要原因是为了保持对三端:iOS、Android和开发工具模拟器的良好兼容性)。

开启上传代码时样式自动补全
开启此选项,开发工具会自动检测并补全缺失样式,保证在iOS 8上的正常显示。

开启代码压缩上传
开启此选项,开发工具在上传代码时将会帮助开发者压缩JavaScript代码,减小代码包体积。

监听文件变化,自动刷新开发者工具
开启此选项后,如果代码发生了改变(需要Ctrl+S先保存),小程序开发工具会自动帮助开发者刷新调试模拟器,从而提高开发效率。也就是说,开发者不再需要手动地点击编译按键即可实时预览小程序运行效果。

开发环境不校验请求域名以及TLS版本
开启此选项,开发工具将不会校验安全域名以及TLS版本,帮助在开发过程中更好地完成调试工作。在开发工具里如果选取了“无AppID”模式,那么开发工具的安全限制级别非常低,不需要使用https访问服务器,也不会校验TLS版本。但如果我们填入了“AppID”,那么默认情况下开发工具的校验行为会和真机环境保持一致,比如,必须使用https访问服务器且服务器域名必须加入到微信小程序账号中的可信任域名列表中。如果我们不想接受这样的限制,又想使用“AppID模式”,那么需要把“开发环境不校验请求域名以及TLS版本”这一项给勾选上。

ES6转ES5的转换,只会帮助开发者处理语法上的问题,新的ES6的API例如Promise等需要开发者自行引入Polyfill或者别的类库。同时,为了提高代码质量,在开启ES6转换功能的情况下,默认启用JavaScript严格模式。开发者可以自己查阅资料了解什么是JavaScript的严格模式。

2.3.8 快速打开官方API文档

小程序提供了一个快速打开官方文档的方法,即点击开发工具左上角的【帮助】→【关于】,在弹出的“关于”对话框中点击【点击打开文档】,即可马上进入官方API文档。

官方API文档除了用于经常查阅API外,推荐大家在版本更新后第一时间去查看更新内容。最新的更新内容对我们开发者来说尤其重要,更新内容通常会出现在官方API文档的【工具】→【下载】以及【历史更新日志】里。

2.3.9 开发工具的更新

微信小程序开发者工具将自动更新,无须开发者手动更新。每次启动开发工具后,如果有新版本则会自动提示下载。
在Windows下,如果开发者重启开发工具后依然没有自动更新,请退出开发工具,右键点击开发工具图标,选择【以管理员身份运行】。以上解决方案在Windows 10下测试可行。如果以上方案都不可用,请前往官网下载并安装最新版本的开发工具。

2.3.10 常用小程序快捷键

1、格式调整
Ctrl+S:保存文件
Ctrl+[,Ctrl+]:代码行缩进
Ctrl+Shift+[,Ctrl+Shift+]:折叠打开代码块
Ctrl+C Ctrl+V:复制粘贴,如果没有选中任何文字则复制粘贴一行
Shift+Alt+F:代码格式化
Alt+Up,Alt+Down:上下移动一行
Shift+Alt+Up,Shift+Alt+Down:向上向下复制一行
Ctrl+Shift+Enter:在当前行上方插入一行
Ctrl+Shift+F:全局搜索

2、光标相关
Ctrl+End:移动到文件结尾
Ctrl+Home:移动到文件开头
Ctrl+i:选中当前行
Shift+End:选择从光标到行尾
Shift+Home:选择从行首到光标处
Ctrl+Shift+L:选中所有匹配
Ctrl+D:选中匹配
Ctrl+U:光标回退

3、界面相关
Ctrl+\:隐藏侧边栏
Ctrl+m:打开或者隐藏模拟器

第3章 从第一个简单的“Welcome”页面开始小程序之旅

本章涉及内容:
小程序的基本文件结构
CSS的使用限制
自适应单位rpx
全局样式
App.json配置文件
Flex布局

3.1 认识小程序的基本文件结构

官方示例项目的文件及文件结构

根目录下面有3个文件:app.js、app.json和app.wxss。一个小程序项目必须有这3个描述App的文件,它们必须放在应用程序的根目录下,否则小程序会提示找不到app.json文件。

app.js、app.json和app.wxss的含义
这3个文件是应用程序级别的文件。
接着是和这3个应用程序级别文件平行的pages文件夹。一个小程序由若干个页面文件构成。
.js、.wxml、.wxss和.json文件的含义

wxml文件类似于我们熟悉的HTML文件,用来编写页面的标签和骨架,不同的是wxml文件里的标签元素不可以使用HTML标签,只能使用小程序自己封装的一些组件,这些组件也是我们后面要重点学习的知识。
wxss文件的作用类似于我们熟悉的CSS文件,用于编写小程序的样式,实际上小程序的样式编写语言就是CSS,只是把.css文件换成了.wxss文件。
json文件用来配置页面的样式与行为。
js文件类似于我们前端编程中的JavaScript文件,用来编写小程序的页面逻辑。
以上4种类型的页面文件的文件名称必须相同,这是要注意的一个地方。
utils文件夹,这个文件夹用来存放一些公共的js文件。

3.2 开始动手编写第一个小程序页面

在app.json 中注册 welcome 页面
注册welcome页面

如果有多个页面,需要将每个页面的路径加入到pages这个数组下,否则小程序不会加载这些页面。
多页面
页面的添加或者删除都需要在pages数组下面增减对应的页面路径,否则小程序会报错。

即使welcome.js文件是一个空文件,依然需要在welcome.js中主动调用一下Page()方法。即使你目前不想在.json文件中配置任何属性,也需要加入一个空的{},以保证小程序能正确执行。

如果app.json文件下pages数组里的页面路径,指向的是一个不存在的文件,那么MINA框架会自动创建这个页面的4个文件。

3.3 构建welcome页面的元素和样式

welcome.wxml

这段代码总共使用了3个微信小程序的组件,分别是
view、text和image组件。
view组件通常作为容器来使用,类似于HTML中的div标签;
text组件用来显示一段文本,类似于HTML中的span标签;
image组件用来显示一张图片,类似于HTML中的img标签。

图片默认宽度300px、高度225px

相对路径与绝对路径在小程序中同样有相对路径和绝对路径的区别。
它以“/”开头,“/”代表根目录。

1
<image src="../../images/avatar/avatar-1.png"></image>

路径中的“..”表示向上一级。

真实项目中,图片资源尽量不要存储在小程序的目录中,因为小程序的大小不能超过1MB,超过则无法真机运行和发布项目。应该将图片都存放在服务器上,让小程序通过网络来加载图片资源。

3.4 小程序所支持的CSS选择器

CSS选择器描述

同时需要注意的是,本地资源在wxss中是无法使用的。比如background-image,如果使用的是本地图片,则无法显示,可以使用网络图片来代替本地图片。

3.5 Flex布局

Flex布局是W3C组织在2009年提出的一个新的布局方案,其宗旨是让页面的样式布局更加简单,并且可以很好地支持响应式布局。这并不是小程序所独有的技术,它本身是CSS语法的一部分。只不过早期时候,主流的浏览器对Flex布局的支持并不完善,造成了很多开发者不知道有这种布局的存在或者使用非常少,我们还是习惯使用传统的position和float属性来布局。但传统的布局方式有它的缺陷,比如像垂直居中就不是那么容易实现,Flex可以很好地解决这些问题。

这里推荐一个学习方法。编程里的知识点是非常细小而琐碎的,学习不同的知识应该掌握不同的方法。对于学习CSS这类知识,笔者认为较好的学习方法应该是在实践中学习。比如Flex布局的学习,我们首先应当大致浏览一下整个Flex的知识树,知道Flex解决了什么问题,有什么特点,大致有几类属性就够了。当我们在做项目遇到布局问题时,脑海里就能意识到Flex可能可以解决这个问题。接着我们抱着试试看的心态,带着目的去查找Flex布局的相关资料,即解决了问题,又能在实践中加深对Flex布局的理解,这比单纯死记硬背效果要好很多。人脑总是对形象化的东西记忆特别深刻,所以我们应当尽量在实践中学习知识。当然,也有可能Flex不能解决问题,但你查找和尝试解决问题的这个过程本身就是很好的学习手段。

小程序自适应单位rpx简介

在小程序里,长度单位既可以使用rpx,也可以使用px。使用rpx可以使组件自适应屏幕的高度和宽度,但使用px不会。要透彻地理解rpx需要对移动端分辨率有一定的了解,比如物理分辨率px、逻辑分辨率pt等概念。

建议以iPhone 6的宽度750个物理像素作为标准,来做设计图。在此宽度下,这张设计图里每个元素的尺寸转换到小程序样式时,转换比例为1物理像素=1rpx=0.5px。rpx和px就是小程序样式里可以使用的两种长度单位。

最后,我们为什么要强调最好是在iPhone 6的尺寸下做设计图呢?因为只有在iPhone 6的尺寸下,设计图里的1个像素才满足下面的转换关系:1物理像素=1rpx=0.5px如果不以iPhone 6的标准来做设计图,也是可以的。但非iPhone 6的尺寸下,设计图与rpx、px的转换关系就不是整数倍的,计算起来比较麻烦,所以建议设计图最好以iPhone 6的尺寸标准来设计,这样换算起来很方便。这也是官方建议的一个设计标准。

如果我们足够细心,可以看到小程序的模拟器选择项下,给出了每种机型的分辨率。要强调的是,这里的分辨率指的是逻辑分辨率pt,而非物理分辨率。以iPhone 6为例,模拟器里给出的分辨率是:375×667;Dpr:2它的意思是:iPhone 6的水平方向有375个逻辑像素点,而竖直方向有667个逻辑像素点,每个逻辑像素点包含2个物理像素点。开发者一定要注意逻辑像素和物理像素的区别。我们通常在PS里做的设计图,它的像素可以简单理解为物理像素。再次提醒开发者,1物理像素不等于1px。假设有一张图片在操作系统下显示宽度为750个像素,我们现在想让这个图片水平方向充满整个页面。如果你直接在页面里(iPhone 6模拟机型下)将图片宽度设置为750px,这是不对的。正确的设置方法为750rpx或者375px,才能让图片水平填满小程序。因为,iPhone 6下:1物理像素=1rpx=0.5px。

3.7 全局样式文件 app.wxss

把公共样式放在 app.wxss 文件内。

3.8 页面的根元素page

page代表着整个页面的容器,如果想对页面整体做样式或者属性设置,那么应该考虑page这个页面的根元素。

3.9 app.json中的window配置项

设置window的属性navigationBarBackgroundColor值为#ECC0A8

第4章 文章列表页面

本章涉及内容:
swiper组件构建banner轮播图以及swiper组件的其他属性
详细介绍image组件的4种缩放模式与9种裁剪模式
数据绑定
常用事件

4.1 文章列表页面元素分析及准备工作

文章页面主体部分由两部分构成,上半部分是一个轮播图,下半部分是文章列表。

轮播图效果,小程序已经提供了一个现成的组件——swiper。
小程序启动后显示的首页,由app.json文件里pages数组的第一个元素决定。

4.2 swiper组件

同时,需要注意的是,swiper组件的直接子元素只可以是swiper-item,如果放置其他组件,则会被自动删除。但swiper-item下是可以放置其他组件或者元素的。

4.3 Boolean值的陷阱

vertical=”false”
vertical=”true”
vertical=”aaa”
vertical=”bbb”
从上面的属性举例中找出原因了:
false并不是Boolean类型,而是一个字符串。只要不是空字符串,那么在JavaScript里都会认为这是一个true。

不加入vertical属性
vertical=” “
vertical=”false“
以上小程序都会认为vertical属性设置成了false

4.4 构建文章列表的骨架和样式

4.5 image组件的4种缩放模式与9种裁剪模式

4种缩放模式和9种裁剪模式如果从理论上完全精确理解,还是有稍许的难度的。但这里笔者建议各位开发者,没有必要完全从理论上搞清楚这些模式。当遇到具体问题时,尝试多去更换几个属性,找到最适合自己需求的属性即可。



相关资料

小程序开发原理与实战

Flex 布局教程:语法篇
页面配置
全局配置
swiper
image
sitemap 配置

Vue.js 快跑:构建触手可及的高性能 Web 应用

第 1 章 Vue.js 基础

为什么选择 Vue.js

使用框架,项目代码可维护程度提高,简化了大部分工作。

使用 Ajax 下载一个列表数据并在页面上显示。

jQuery

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<ul class="js-items"></ul>

<script>
$(function(){
$.get('https://example.com/items.json')
.then(function(data){
ar $itemUL = $('.js-items');
if(! data.items.length){
var $noItems = $('li');
$noItems.text('Sorry,there are no items');
$itemsUl.append($noItems);
}else{
data.items.forEach(function(item){
var $newItem = $('li');
$newItem.text(item);
if(item.includes('blue')){
$newItem.addClass('is-blue');
}
$itemUl.append($newItem);
})
}
})
})
</script>

这段代码执行了以下逻辑:
1、使用 $.get() 发起 Ajax 请求。
2、选取类名为 .js-items 的元素并将它存入 $itemUl 变量。
3、如果下载的列表中没有列表项,则创建一个 li 元素,设置这个 li 元素的文本节点以表示当前没有列表项,然后将它添加到文档流中。
如果列表中存在列表项,则循环遍历每一个列表项。
4、为列表中的每一项创建一个li元素,并将其文本节点设置为该列表项的值。然后判断该列表项是否包含blue字符串,如果包含则将li元素的类名设置为is-blue。最后,将li元素添加到文档流中。

Vue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<ul class='js-items'>
<li v-if='! items.length'> Sorry, there are no items. </li>
<li v-for="item in items" :class="{'is-blue': item.includes('blue')}">
{{item}}
</li>
</ul>

<script>
new Vue({
el: '.js-items',
data: {
items: []
},
created(){
fetch('https://example.com/items.json')
.then((res) => res.json())
.then((data) =>{
this.items = data.items;
})
}
})
</script>

这段代码执行了以下逻辑:
1、使用fetch()发起一个Ajax请求。
2、将返回的JSON数据解析为JavaScript对象。
3、将下载的列表项存储为data对象中的items属性。

vue-router 用于路由控制——根据应用的不同URL来显示不同的内容。
vuex 用于状态管理——通过一个全局数据中心在组件间共享数据。
vue-test-utils 用于 Vue 组件的单元测试。

阅读更多

Vue 企业开发实战

第 1 章 大觅项目架构设计

1.2.2 什么是 NPM

Node.js 的包管理器 NPM 是全球最大的开源库生态系统,它集成在 Node.js 中,在安装 Node.js 的时候就已经自带了 NPM 包管理工具。验证 NPM 是否安装成功的方法同验证 Node.js 的方法。

在命令行窗口中输入“npm -v”可得到 NPM 的版本,说明 NPM 已经安装成功,如图 1.10 所示。

NPM 常用命令

1
2
3
4
npm install<Module Name>-g   //安装模块 加不加“-g”代表是不是全局安装
npm list<Module Name>     //查看某个模块的版本号
npm uninstall<Module Name>   //卸载模块
npm update<Module Name>    //更新模块
注意: 全局安装和非全局安装的区别: 举例说明,使用“npm install express -g”安装express框架后,就可以在计算机的任意一个文件夹下打开命令提示符窗口,直接使用express创建项目,否则会遇到“'express'不是内部或外部命令,也不是可运行的程序”错误。读者应该也遇到过类似的问题,这就是非全局安装引起的,使用“npm install express”安装express框架为局部安装,局部安装就是将模块下载到当前命令行所在目录下,只有在当前目录下才可以用。

1.2.3 安装 cnpm

NPM 安装插件需要从国外服务器下载,受网络影响大,下载比较慢,容易出现异常。说明来自淘宝 NPM 镜像官网:这是一个完整的 npmjs.org 镜像,可以用此代替官方版本(只读),同步频率目前为 10 分钟一次,以保证尽量与官方服务同步。既然淘宝 NPM 镜像这样方便,那么该如何使用呢?使用方法比较简单,只需要在命令行中输入以下内容,按回车键等待安装成功即可。npm install -g cnpm –registry=https://registry.npm.taobao.org安装CNPM成功之后,输入“cnpm -v”。

1.2.3 Vue-cli 脚手架

Vue-cli 是一个官方命令行工具,可用于快速搭建大型单页面应用。该工具提供开箱即用的构建工具配置,带来现代化的前端开发流程,只需几分钟即可创建并启动一个带热重载、保存时静态检查,以及可用于生产环境构建配置的项目。

单页面应用(Single Page Web Application,SPA)只有一个 Web 页面的应用,是一种从 Web 服务器加载的客户端,单页面跳转仅刷新局部资源,公共资源(js、css 等)仅需加载一次。
多页面应用(Multi-Page Application,MPA)多页面跳转刷新所有资源,每个公共资源(js、css 等)需选择性重新加载。

全局安装 Vue-cli

1
cnpm install vue-cli -g

命令交互的含义如下:
➢ project name:如果输入新的项目名称,则会使用新输入的项目名称;直接回车的话,就会默认使用 webpack 后面跟的项目名称。
➢ Project description:项目的描述内容,可以自定义一些内容。
➢ Author: 作者,可以写上自己的邮箱或者 GitHub 的地址。
➢ Vue build:打包的方式,这里直接回车即可。
➢ Install vue-router? (Y/n):是否安装 Vue 路由,建议选择 Yes,一般项目都需要路由功能。
➢ Use ESLint to lint your code? (Y/n):是否启用 ESlint 检测,选择不启用。
➢ Set up unit tests (Y/n):是否需要单元检测,建议选择不需要。
➢ Setup e2e tests with Nightwatch? (Y/n):是否需要端对端的检测,建议选择不需要。

使用原始的方式进行开发有什么问题呢?看一下原始开发存在的以下问题。
➢ 难以维护
➢ 加载缓慢
➢ 体验差
➢ 重复编码
➢ 扩展困难
➢ 前后端耦合度高

分层架构的主要优势在于:易维护、可扩展、易复用、灵活性高,因此深受前端开发工程师喜爱。

模块化。模块化是指解决一个复杂问题时自顶向下逐层把系统划分成若干模块的过程,有多种属性反映其内部特性,同时模块化还可以解耦实现并行开发。主要的模块化解决方案有:AMD(requirejs)、CMD(seajs)、CommonJS、ES6。模块化用来分割、组织和打包软件。

分离方式
➢ 不分离
➢ 部分分离
➢ 完全分离 

不分离和部分分离有什么缺点呢?
不分离:前后端共用同一个项目目录,本地开发环境搭建成本高,项目比较复杂、不宜维护且维护成本高、发布风险高,不利于问题的定位和修改。
部分分离:本地环境搭建成本较高,需要后端提供页面模板(JSP 等),更新和修改模板需要后端人员操作,效率低且不易维护,发布方式需要同时发布,且沟通成本比较高。

完全分离又分为两种:分离开发集成部署和分离开发分离部署。

分离开发分离部署。前端使用纯 HTML 通过接口的方式进行数据的交互,降低系统的复杂度,部署时单独部署到一台服务器上,使用代理进行数据的交互。

1.3.4 UI 框架

UI 框架的优缺点。

优点:
➢ 快速搭建 Web 页面
➢ 集中精力完成业务代码
➢ 缩短开发周期

缺点:
➢ 冗余代码
➢ 无法定制化、精细化开发

UI 框架的选择标准
UI 框架是否能够满足项目要求
组件丰富度,效果炫酷度
UI 框架的 API 完整度、社区的活跃程度

说明
–save 与–save-dev 的区别
–save 会把依赖包添加到 package.json 文件 dependencies 下
–save-dev 会把依赖包添加到 package.json 文件 devDependencies 下。

dependencies 是产品上线运行时的依赖,
devDependencies 是产品开发时的依赖。
devDependencies 下的模块是产品开发时用的。

在项目中引入 iView 框架
引入方式一般是在 Webpack 的入口页面 main.js 中做如下配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import Vue from 'vue';
import VueRouter from 'vue-router';
import App from 'components/app.vue';
import Routers from './router.js';

// 引入iView框架
import iView from 'iview';
// 引入iView框架样式
import 'iview/dist/styles/iview.css';
Vue.use(VueRouter);
Vue.use(iView);
// The routing configuration
const RouterConfig = { 
routes: Routers
};
const router = new VueRouter(RouterConfig);
new Vue({  
el: '#app',  
router: router,  
render: h => h(App)
});

组件是什么?
组件是将一个或几个完成各自功能的代码段封装为一个或几个独立的部分。用户界面组件就包含了这样一个或几个具有各自功能的代码段,最终完成了用户界面。
另外一种方式是按需引入组件,也就是项目中需要什么组件,就引入什么组件,实现按需加载,减少文件体积。

按需引入的加载方式:
安装插件

1
npm install babel-plugin-import --save-dev

babel-plugin-import 插件可以从组件库中引入需要的模块,而不是把整个库都引入,从而提高性能。

然后在.babelrc 文件中添加代码:

1
2
3
4
5
6
7
"plugins": [
["import", {  
"libraryName": "iview",
"libraryDirectory": "src/components" 
}]
]
}

Vue.js 项目实战

Vue 开发入门

Vue 是一个专注于构建 Web 用户界面JavaScript 库。

1.1 为什么需要另外一个前端框架

Vue 易用、灵活、速度快、还提供了许多功能和可选工具,这使得开发者能够快速地构建一个现代 web 应用。Vue 的作者尤雨溪将其称为渐进式框架

  • Vue 遵循渐进增量的设计原则,其核心库专注于用户界面,使得现有的项目可以方便地集成使用 Vue。
  • Vue 既可以构建出很小的原型,又可以构建出复杂的大型 web 应用。
  • Vue 非常容易上手 —— 初学者能轻松掌握 vue,而已经熟悉 Vue 的开发者则可以在实际项目中快速发挥出它的作用。

Vue 整体上遵循 MVVM(Model-View-ViewModel,模型 - 视图 -视图模型)架构,也就是说 View(用户界面或视图)和 Model(数据)是独立的,ViewModel(Vue)是 View 和 Model 交互的桥梁。Vue 对 View 和 Model 之间的更新操作做了自动化处理,并且已经为开发者进行了优化。

Vue 还吸收了其他类似框架( 如 React、Angular、Polymer)的精华。

Vue 核心功能概述

  • 一个响应式的数据系统,能通过轻量级的虚拟 DOM 引擎和最少的优化工作来自动更新用户界面。
  • 灵活的视图声明,包括优雅友好的 HTML 模板、JSX(在 JavaScript 中编写 HTML 的技术)以及 hyperscript 渲染函数(完全使用 JavaScript )。
  • 由可维护、可复用组件组成的组件化用户界面。
  • 官方的组件库提供了路由、状态管理、脚手架以及更多高级功能,使 Vue 成为了一个灵活且功能完善的前端框架。

Weex、NativeScript

1.1.2 兼容性需求

Vue 不支持 Internet Explorer 8 以下版本,因为 Vue 使用了 JavaScript 中相对较新的特性,比如 Object.defineProperty,
而它们在老版本的浏览器中是无法 polyfill 的。

编译器 Babel ,它编译过的代码可以很好地运行在老版本浏览器中。

1.3 创建一个应用

整个库都是基于 Vue 实例的,而实例是 View 和 Model(数据)交互的桥梁。因此需要创建一个新的 Vue 实例来启动应用。

1
2
3
4
5
6
7
8
9
10
11
//  创建 Vue 实例
var app = new Vue({
// 根 DOM 元素的 CSS 选择器
el: '#root',
// 一些数据
data(){
return {
message: 'Hello Vue.js'
}
}
})

使用关键字 new 调用 Vue 构造器创建了一个新的实例。Vue 构造器有一个参数 —— option 对象。该参数可以携带多个属性(称为选项)。

通过 el 选项,我们使用 CSS 选择器告知 Vue 将实例添加(挂载)到 Web 页面的哪个 DOM 元素中。
也可以 Vue 实例的 $mount 的方法代替el选项:
app.$mount(‘#root’)。

1
2
3
4
5
6
7
8
9
var app = new Vue({
data(){
return {
message: 'Hello Vue.js'
}
}
})
// 添加 Vue 实例到页面中
app.$mount("#root");

Vue 实例的大多数特殊方法和属性都是以美元符号($)开头的。

在单个 Web 页面中,开发者可以添加任意多个 Vue 应用。只需要为每个应用创建出
新的 Vue 实例并挂载到不同的 DOM 元素即可。当想要将 Vue 集成到已有的项目中时,这非常方便。

Vue 开发者工具

Vue 有一个官方调试工具,在 Chrome 中以扩展的方式呈现,名为 Vue.js devtools。通过该工具可以看到应用的运行情况,这有助于调试代码
。可以在 Chrome 网上应用商品下载;如果使用 Firefox,则可以到 Firefox 附加组件 下载。
使用 Chrome 版本的话,还需要进行额外的设置。在扩展设置中,启用 Allow access to file URLs 选项。这样调试工具就能在从本地磁盘打开的 Web 页面上检测 Vue 了。

可以将 devtools 选项卡拖放到喜欢的位置。建议将其放在靠前的位置,因为当 Vue 不处于开发模式或没有运行时,该选项卡在页面中是隐藏起来的

可以通过 name 选项修改 Vue 实例的名字:

1
2
3
4
var app = new Vue({
name : 'MyApp',
// ...
})

当一个页面中有多个 Vue 实例时,这有助于直观地在开发者工具中找到具体的某个实例。

1.4 借助模板实现 DOM 的动态性

模板是描述 View 最简单的方法,只需要少量额外的语法就能轻松实现 DOM 的动态更新。

1.4.1 文本显示

文本插值用于在 Web 页面中显示动态的文本。文本插值的语法是在双花括号内包含单个任意类型的 JavaScript 表达式。当 Vue 处理模板时,该 JavaScript 表达式的结果将会替换掉双花括号标签。

1
2
3
<div id="root">
{{ message }}
</div>

DOM 和 数据连通了

1
app.message = 'Awesome!'

数据绑定,每当数据有改变时, Vue 都能够自动更新 DOM,Vue 框架中包含一个非常强大且高效的响应系统,能对所有的数据进行跟踪,并且能在数据发生改变时按需自动更新 View。

1.4.2 利用指令添加基本的交互

Vue 中所有的指令命名都是带 V- 前缀的,并遵循短横线分隔式(kebab-case)语法。这个意味着要用短横线将单词分开。HTML 属性是不区分大小写的。

1
2
3
4
5
<div id="root">
<p>{{message}}}</p>
<!-- 添加一个文本输入框 -->
<input v-model = "message">
</div>

双向数据绑定 v-model

项目 1:Markdown 笔记

v-model 指令不限于文本输入使用。它同样可以用于其他元素,例如勾选框、单选按钮,甚至自定义组件。

2.1.3 预览面板

计算属性 通过它可以定义一个新的属性,而该属性可以结合任意多个属性,并做相关转换操作。

  • 计算属性的值基于它的依赖进行缓存,因此如果没有必要是不会重新运行函数的,从而有效防止无用计算;
  • 当函数中用到的某个属性发生了改变,计算属性的值也会根据需要自动更新;
  • 计算属性可以如其他普通属性一起使用(可以在其他计算属性中使用计算属性);
  • 计算属性只有真正用于应用中时,才会进行计算操作。
在应用内,不建议使用 v-html 指令对用户提供的内容做 HTML 插值。这是因为用户可能会在标签中编写不怀好意、会被执行的 JavaScript 代码。当然对普通文本做插值是安全的,因为 HTML 不会被执行。

元素中的任意内容将被 v-html 指令的值替代。可以利用这一点来放置占位符内容。

对于文本插值,v-text 是一个与 v-html 等效的指令。它的行为与 v-html 类似,只不过会对 HTML 标签做转译处理,形同典型的文本插值。

2.1.4 保存笔记

localStorage 保存笔记

侦听改变

监听器 watch 选项是一个字典,把被侦听属性的名字作为键,把侦听选项对象作为值。这个对象必须要一个 handler 属性,该属性可以是一个函数,也可以是一个方法的名字。这个处理函数将接收两个参数:被侦听属性的新值和旧值。

1
2
3
4
5
6
7
8
9
10
new Vue({
// 侦听 content 数据属性
watch: {
content: {
handler(val,oldVal){
console.log('new note:', val , 'old note' , oldVal)
}
}
}
})

还有另外两个选项可以和 handler 一起使用。

  • deep 是一个布尔类型,告诉 Vue 以递归的方式侦听嵌套内部值的变化。
  • immediate 也是一个布尔类型,会立即触发调用处理函数,而不用等到属性第一次变化时才调用。
这两个选项的默认值都是 false,所以不需要使用的时候,可以完全忽略它们。 当不需要其他选项(例如 deep 或 immediate)时,这是侦听器中最常用的语法。
1
2
3
4
watch: {
content(val,oldVal){
}
}

保存笔记

1
localStorage.setItem('content',val)

复用方法
良好的编程准则之一:不要重复自己(DRY),也称为一次仅且一次。开发者应该遵守这个准则。因此可以把一些逻辑写在
可复用的函数里面:methods。

1
2
3
4
5
6
7
8
9
10
11
12
new Vue({
watch:{
content: 'saveNote'
},
methods:{
saveNote(val){
console.log('saving note:',this.content);
localStorage.setItem('content',thie.content)
}
}
})

访问 Vue 实例

在 methods 内部,可以通过 this 关键字访问 Vue 实例,还可以访问 Vue 实例的其他属性或特殊函数。

基本上可以在任意函数(方法、处理函数或其他钩子)其中使用 this 关键字访问 Vue 实例。 生命周期钩子

生命周期钩子

每个 Vue 实例都严格遵守一个生命周期,包括多个环节:创建、挂载到页面、更新,最终被销毁。例如,在创建实例阶段,Vue 会将实例数据变成响应式数据。

钩子是一组特殊的函数,会在某个时间点被自动调用。这就允许我们自定义框架的逻辑。例如在创建 Vue 实例时调用一个方法。

在每个环节之中或之前,有很多钩子可以用于执行逻辑。

  • beforeCreate:在 Vue 实例被创建时(例如使用 new Vue({ }))、完成其他事项之前调用。
  • created:在实例准备就绪之后调用。注意,此时实例还没有挂载到 DOM 中。
  • beforeMount:在挂载(添加)实例到 web 页面之前调用。
  • mounted:在实例被挂载到页面并且 DOM 可见时调用。
  • beforeUpdate:当实例需要更新时(一般来说,是当某个数据或计算属性发生改变时)调用。
  • updated:在把数据变化应用到模板之后调用。注意此时 DOM 可能还没有更新。
  • beforeDestroy:在实例销毁之前调用。
  • destroyed:在实例完全销毁之后调用。
在 JavaScript 中,如果值为 false、0、空字符传、null、undefined 或 NaN(不是一个数),则它就是假值。在浏览器的本地存储数据中,如果对应的键不存在, localStorage.getItem() 方法会返回 null。

在数据中直接初始化

1
2
3
4
5
6
7
new Vue({
data(){
return{
content:localStorage.getItem('content') || 'You can write in **markdown**'
}
}
})

2.2 多条笔记

2.2.1 笔记列表

添加新建笔记的方法
每一条笔记都是具体如下数据的对象。

  • id :笔记的唯一标识符。
  • title :笔记的标题,用来显示在笔记列表中。
  • content : 笔记的 Markdown 格式内容。
  • created : 笔记创建的日期。
  • favorite : 这是一个布尔型,用于表示是否收藏了笔记,已收藏的笔记显示在笔记列表的顶部。

选择当前时间(也就是从 1970 年 1 月 1 日 00:00:00 UTC 开始经过的毫秒数)作为区分笔记的唯一标识符,这是一种不错的方式。

用 v-on 实现按钮的单击事件

1
<button v-on:click="addNote"></button>

绑定事件 v-on: 简写 @

1
<button @:click="addNote"></button>

用 v-bind 绑定属性

1
<button v-bind:title="notes.length + ' note(s) already'">

属性绑定 v-bind: 简写 :

1
<button :title="notes.length + ' note(s) already'">
当需要更新属性值的时候,用 v-bind 指令绑定的 JavaScript 表达式会自动重新运算。

用 v-for 显示列表

1
2
3
<div class="notes">
<div class="note" v-for="note of notes">{{note.title}}</div>
</div>

2.2.2 选择一条笔记

动态 CSS 类

1
<div :class= "{ one: note === selectedNote }"></div>
可以把静态与动态的 class 属性结合起来。建议将非动态的类放到静态的属性中,Vue 会对静态值做优化处理。

条件指令 v-if
v-else-if
v-else

标签不会出现在 DOM 中。用于对实际的元素进行重新组合。

侦听器默认值侦听目标对象的直接变化:赋值、数组添加、删除、移动。
例如:

1
2
3
4
5
6
7
8
9
//  赋值
this.selectedId = 'abcd'

// 在数组中添加或删除某项
this.selectedId.push({...})
this.selectedId.splice(index,1);

// 数组排序
this.notes.sore(...)

操作不会触发侦听器

1
2
3
4
5
6
//  给某个属性或者嵌套对象赋值
this.myObject.someAttribute = 'abcd';
this.myObject.nestedObject.otherAttribute = 42

// 修改数组中某项的内容
this.notes[0].content = 'new content'

这种情况下,需要在侦听器上添加 deep 选项

1
2
3
4
5
6
7
watch: {
notes: {
handle : 'saveNotes',
// 需要使用这个选项来监听数组中每个笔记属性的变化
deep: true
}
}

按照收藏、时间排序

1
2
3
4
5
6
7
computed: {
sortedNotes(){
return this.notes.slice()
.sort((a,b) => a.created - b.created)
.sort((a,b) => (a.favorite === b.favorite) ? 0 : a.favorite ? -1 : 1)
}
}
由于 sort 方法会直接修改源数组,这里使用 slice 方法创建新的副本。这样可以防止 notes 监听器。

创建日期过滤器

(1)引入 momentjs

1
<script src="https://unpkg.com/moment"></script>

(2) 使用 Vue.filter 全局方法(不在 Vue 实例的创建代码中,比如位于文件开头)注册过滤器

1
Vue.filter('date',time=> mement(time).format('DD/MM/YY, HH:mm'))
1
<span class="value">{{ selectedNote.created | date }}</span>

文本统计

1
2
3
4
5
6
7
8
9
10
11
12
//  计算换行符的格式 
str.split(/\r\.|\r|\n/).length
// 将换行符转换为空格
str.replace(/\n/g, '');
// 排除开头和结尾的空格
str.replace(/(^\s*)|(\s*$)/gi, '')
// 将多个重复空格转换为一个
str.replace(/\s\s+/gi, ' ');
// 返回空格数量
str.split(' ').length;
// 字符数量
str.split('').length

项目 2:城堡决斗游戏

在 Vue 实例构造器之后,添加一个事件监听器到 window 对象中,监听浏览器窗口大小的变化。

1
2
3
4
//  窗口大小变化的处理
window.addEventListener('resize', () => {
state.wordRatio = getWorldRatio();
}

3.3.3 万能的组件

组件是构建应用的基础模块,是 Vue 应用的核心概念。组件是视图的一个个小部分,因此相对来说应用比较小、可复用,并且尽可能地自给自足。采用组件构建应用有助于应用的维护和升级,特别是当应用规模变大之后。实际上,这已经成为了高效、可控地开发大型 Web 应用的标准方法。

组件

可以使用全局函数 Vue.component() 来注册组件。该函数接受两个参数:一个是注册组件的名称,另一个则是组件的定义对象本身,它与 Vue 实例使用相同的选项。

1
2
3
Vue.component('top-bar',{
template:`<div class='top-bar'>Top bar</div>`
})

使用 prop 进行父组件到子组件的通信

利用 props 选型可以将 prop 添加到组件中。

1
2
3
4
5
6
7
8
Vue.component('top-bar',{
props: ['players','currentPlayerIndex','turn'],
created(){
console.log(this.players);
}
})

<top-bar :turn="turn" :current-play-index="currentPlayIndex" :players="players">
注意:由于 HTML 是不区分大小写的,建议对 prop 的名字使用短航线命名方法(kebab-case),而在 JavaScript 代码中使用驼峰式命名方法(camel-case)。

模板中的 prop

通过 players prop 显示玩家姓名。

1
2
3
4
5
6
template:`
<div class="top-bar">
<div class="player p0">{{ players[0].name}}</div>
<div class="player p1">{{ players[1].name}}</div>
</div>
`

在组件上监听原生事件
为监听到组件的 click 事件,需要对 v-on 指令使用 .native 修饰符。

1
<card :def="testCard" @click.navtive="handlePlay">

使用自定义事件进行子组件到父组件的通信
在组件内部,使用 $emit 这个特殊方法触发的事件可以被父组件捕获到。该方法接收一个固定的参数,即事件类型:

1
this.$emit('play','orange',42)

可以使用名为 $on 的特殊方法监听自定义事件:

1
2
3
4
5
6
7
8
9
10
11
this.$on('play', () => {
console.log('Caught a play event!');
})

<card v-on:play="handlePlay"/>>
// 简写:
<card @play="handlePlay"/>

handlePlay(color,number){
console.log('handle play event','color = ' , color ,'number=',number)
}
Vue 的自定义事件与浏览器事件系统是完全分开的。方法 $on 和 $emit 并不是 addEventListener 和 dispatchEvent 的别名。这也解释了为什么在组建中需要使用 .native 修饰符来监听浏览器事件(如 click)。 大多数情况下,最好使用自定义事件完成组件之间的通信。

hand 组件的动画过度效果

动画效果:CSS 过渡结合 <transition> 组件。当添加或移除元素时,使用 v-if 或 v-show 指令来帮助实现 CSS 过渡。

1
2
3
4
5
//  应用状态集合
var state = {
// 用户界面
activeOverlay: null
}
1
2
3
<transition>
<hand v-if="!activeOverlay"/>
</transition>

<transition> 特殊组件不会显示在 DOM 中。

当元素被添加到 DOM 时(进入阶段),<transition> 组件会自动将下列 CSS 类应用到元素中。

v-enter-active: 当进入过渡状态被激活时,会应用该类。在元素插入 DOM 之前,添加该类到元素中,并在动画结束时移除它。应该在这类中添加一些 transition CSS 属性并定义其过渡时长。

v-enter: 元素进入过渡的开始状态。在元素插入 DOM 之前,添加该类到元素中,并在元素被插入的下一帧移除。例如,你可以在这个类中设置透明为 0。

v-enter-to: 元素进入过渡的结束状态。在元素插入 DOM 后的下一帧添加,同时 v-enter 被移除。当动画完成后,v-enter-to 会被移除。

当元素从 DOM 中移除时(离开阶段),<transition> 组件会自动将下列 CSS 类应用到元素中。

v-leave-active:当离开过渡状态被激活时,会应用该类。当离开过渡触发时,添加该类到元素中,并在从 DOM 中移除元素时移除它。应该在这个类中添加一些 transition CSS 属性并定义其过渡时长。

v-leave:元素被移除时的开始状态。当离开过渡触发时,添加该类到元素中,并在下一帧移除。

v-leave-to:元素离开过渡的结束状态。在离开过渡触发后的下一帧添加,同时 v-leave 被移除。当从 DOM 中移除元素时,该类也会被移除。

在离开阶段,并不会立即从 DOM 中移除元素。当过渡结束后,才会将其移除,这样用户可以看到动画效果。

进入 v-enter-active
v-enter v-enter-to
透明度0 -> 透明度1

离开 v-leave-active
v-leave v-leave-to
透明度1 -> 透明度0

<transition> 组件会自动检测应用在元素上的 CSS 过渡效果的持续时间。

复用动画

1
2
3
<transition name="fade">
<hand v-if="!activeOverlay" :cards="testHand">
</transition>
1
2
3
4
5
6
7
8
9
10
/* transition.css */
.fade-enter-active,
.fade-leave-acteve {
transition: opacity 1s;
}

.fade-enter,
.fade-leave-to {
opacity: 0;
}

现在只需要通过 <transition name="fade">标签就可以在任意元素上复用这个动画了。

贝塞尔曲线缓动函数,可以使得动画更加平滑。

1
2
3
4
5
6
7
8
9
10
.hand-enter-active .wrapper,
.hand-leave-active .wrapper {
transition: transform .8s cubic-bezier(.08,.74,.34,1);
transform-origin: bottom center;
}

.hand-enter .wrapper,
.hand-leave-to .wrapper {
transition: rotate(90deg);
}

为元素列表添加动画效果,需要使用另外一个特殊的组件 <transition-group>。当元素被添加、移除和移动时,该组件将对它的子元素做出动画效果。 跟 <transition> 元素不同的是,<transition-group>默认情况下会作为<span> 元素出现在 DOM 中。可以使用 tap prop 修改这个 HTML 元素。

1
2
3
<transition-group tag="ul">
<li v-for="item of items" />
</transition-group>

<transition-group> 的子元素必须由唯一的 key 做标识。

1
2
3
4
// 指定过渡效果名称为 card 
<transition-group name="card" tag="div" class="cards">
<card v-for="card of cards">
</transition-grou

特殊的 key 属性
当 Vue 更新存在于 v-for 循环中的 DOM 元素列表时,会尽量最小化应用于 DOM 的操作,例如添加和移除元素。大多数情况下,这是更新 DOM 的一种非常高效的方法,并且对性能的提升也有帮助。
为了做到这一点,Vue 会尽可能地复用元素,并仅对 DOM 中需修改的地方进行最小范围的修改,以达到理想的结果。这意味着重复的元素会被打包到一起,不会在添加或移除列表中的项时移动它们。不过,这也意味着对其应用过渡不会有动画效果。

key 属性为元素指定唯一标识符。

使用插槽分发内容

1
2
3
4
5
<div class="overlay" @click="handleClick">
<div class="content">
<slot />
</dvi>
</div>
1
2
3
<overlay>
Hello world!
</overlay>

Vue 提供了一个特殊的组件可以把其转换为任意的组件:component 组件。只需要将它的 is prop 设置为一个组件名或组件定义对象,甚至是一个 HTML 标签,component 组件就会变为响应的内容:

动态修改组件

1
2
3
<overlay v-if="activeOverlay">
<component :is="'overlay-content-'+activeOverlay" :player="currentPlayer" >
</overlay>

当 targetHeight 属性发生改变时,就开始播放动画

1
2
3
4
5
6
7
8
9
10
11
12
watch:{
targetHeight(newValue,oldValue){
const vm = this;
new TWEEN.Tween({value:oldValue})
.easing(TWEEN.Easing.Cubi.InOut)
.to({value: newValue},500)
.onUpdata(function(){
vm.height = this.value.toFixed(0)
})
.start()
}
}

高级项目设置

4.1 设置开发环境

安装 Node.js 和 npm

4.1.1 安装官方命令行工具 vue-cli

安装 vue-cli 并将其作为一个全局的包:

1
2
3
4
5
npm install -g vue-cli

// 打印 vue-cli 打印版本
vue -V
vue --version

项目脚手架

推荐的官方模板是 webpack 模板,具有使用 Vue 创建整个 SPA(单页面应用)所需的全部功能。使用 webpack-simple 并逐步引入功能。

渲染函数

Vue 使用一个虚拟 DOM 的实现,用树状结构的 JavaScript 对象来构建虚拟 DOM。然后 Vue 将虚拟 DOM 应用到真实浏览器的 DOM 上,所用方法是计算两者之间的差距。这尽可能地避免了 DOM 操作,因为 DOM 操作通常是主要的性能瓶颈。

按照惯例,h 是 createElement 的别名,这是编写 JSX 时非常常见和必须的。它得名于使用 JavaScript 描述 HTML 的技术 - Hyperscript。

createElement(或称 h)方法最多需要 3 个参数,
(1)第一个参数是元素类型。它可以是一个 HTML 标签名称(比如 div),在应用中注册过得组件名称,或者直接就是一个组件定义对象。
(2)第二个参数是可选的。它是一个定义了属性、prop、事件监听器等的数据对象。
(3)第三个参数也是可选的。它可以是简单的纯文本,也可以是一个用 h 创建的其他元素的数据。

示例:

1
2
3
4
5
6
7
8
9
10
11
render(h){
return h('ul',{'class':'movies'},[
h('li',{'class': 'movie'},'Start Wars'),
h('li',{'class':'movie'},'Blade Runner'),
])
}

<ul class='movies'>
<li class='movie'>Star Wars</li>
<li class='movie'>Blade Runner</li>
</ul>

4.2.4 配置 Babel

Babel 是一个 JavaScript 代码编辑工具,以便我们在旧版和最新版的浏览器中使用新特性(如 JSX 或箭头函数)。建议在所有正式的 JavaScript 项目中使用 Babel。

polyfill 是用于检查特性在浏览器中是否可用的代码;如果不可用,它将实现这个特性,使其可以像原生的一样工作。

1.Babel Vue 预览

安装并使用 babel-preset-vue

1
npm i -D babel-preset-vue

主要的 Babel 配置是在项目根目录下已存在的 .babelrc JSON 文件中完成的。

打开这个 .babelrc 文件并将 vue 预览添加到相应的列表中:

1
2
3
4
5
6
7
{
"presets": {
["env",{"modules": false}],
"stage-3",
"vue"
}
}

polyfill

添加 Babel polyfill,以便在旧浏览器中使用新的 JavaScript 特性。

在开发依赖中安装 babel-polyfill 包。

1
npm i -D babel-polyfill

在 src/main.js 文件的开头将其导入

import ‘babel-polyfill’

这将为浏览器启用所有必要的 polyfill。

4.2.5 更新依赖

1、手动更新

1
2
3
4
// 要检查项目中使用的包是否有新的版本,可以在根文件夹中运行以下命令:

npm outdated
npm install
不要忘记阅读你所更新包的更改日志!可能会有你希望了解的破坏性改变或改善。

2.自动更新

1
npm update
该命令只会更新与 package.json 文件中所指定版本兼容的版本。如果你想将包更新为其他版本,则需要手动执行。

3.更新 Vue

更新包含核心库的 Vue 包时,你也应该更新 vue-template-compiler 包。它是使用 webpack (或其他构件工具)时编译所有组件模板的包。

这两个包必须始终处于相同的版本。例如,如果你使用 vue 2.5.3 ,那么 vue-template-complier 也应该是版本 2.5.3.

4.2.6 为生产而构建

1
npm run build

默认情况下,使用 webpack-simple 模板时,它会将 JavaScript 文件输出到项目的 /dist 文件夹中。

4.3 单文件组件

Vue 具有自己的格式,名为单文件组件(SFC)。该格式由 Vue 团队创建,文件扩展名为 .vue。允许每个文件编写一个组件,将模板以及该组件的逻辑和样式集中在一个位置,这里的主要优势在于,每个组件都明显独立,更易于维护,易于共享。

单文件组件使用类似 HTML 的语法描述 Vue 组件。它可以包含 3 种类型的根块

1
2
3
<template>,使用我们已经用过的模板语法描述组件的模板;
<script>,其中包含组件的 JavaScript 代码;
<style>, 其中包含组件使用的样式。

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<template>
<div>
<p>{{ message }}</p>
<input v-model="message" />>
</div>
</template>
<script>
export default {
data() {
return {
message: "Hello world"
};
}
};
</script>
<style>
p {
color: grey;
}
</style>

编辑 main.js 文件,并使用 import 关键字导入单文件组件;

1
import Test from './Test.vue'

移除 render 选项,使用对象展开运算符复制 Test 组件的定义:

1
2
3
4
new Vue({
el: '#app',
...Test
})
组件添加到应用程序的方法:使用 JavaScript 展开运算符,因此 ...App表达式将属性复制到应用定义对象。它的主要优点是,在开发工具中不再用无用的顶层组件;它会成为我们的根组件。

4.3.1 模板