jQuery()函数的4中调用方式
选择元素
$(selector)
$(selector,context)
封装成jQuery对象
$(Element|Document|Window)
创建jQuery对象
$('<img/>')
$('<img/>',{
src: url
})
传入函数,在文档加载完毕运行
DOMContentLoaded
jQuery(function(){});
$(document).ready(function(){});
$(selector)
$(selector,context)
$(Element|Document|Window)
$('<img/>')
$('<img/>',{
src: url
})
DOMContentLoaded
jQuery(function(){});
$(document).ready(function(){});
ES6的Proxy对象可以用来拦截一个Object对象的属性的修改,这次我们利用它来实现一个简单的双向绑定。
废话不多说,请看代码。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>View to model</title>
</head>
<body>
<div>
<!-- 使用'bind-to'的属性来标记这个输入框里的值会对Model里的属性进行修改 -->
<input bind-to="name" />
<input bind-to="name" />
</div>
<!-- 使用#属性名#来把Model里的值映射到DOM中 -->
<span>#name#</span>
<span>#name#</span>
<button onclick="reset()">reset</button>
<script type="text/javascript">
var handler = {
// 拦截属性的设置,触发试图的更新
set: function(target, key, value, receiver) {
target[key] = value;
updateView(propertyName);
return Reflect.set(target, key, value);
},
};
// 双向绑定的对象
var model = new Proxy({}, handler);
// 双向绑定列表
var inputs = {},views = {};
var all = document.all;
// 遍历所有DOM元素
for(var i=0,l=all.length;i<l;i++){
// 找到所有有bind-to的输入框
if(all[i].getAttribute('bind-to')){
var dom = all[i];
var propertyName = dom.getAttribute('bind-to');
inputs[propertyName] = inputs[propertyName] ? inputs[propertyName] : [];
views[propertyName] = views[propertyName] ? views[propertyName] : [];
inputs[propertyName].push(dom);
dom.addEventListener('change',function function_name(e) {
var propertyName = e.target.getAttribute('bind-to');
model[propertyName] = e.target.value;
})
}
// 找到所有model驱动的页面元素
if(all[i].innerHTML && all[i].innerHTML.length>2 &&
all[i].innerHTML[0] == '#' &&
all[i].innerHTML[all[i].innerHTML.length-1] == '#'){
var propertyName = all[i].innerHTML.slice(1,all[i].innerHTML.length-1);
inputs[propertyName] = inputs[propertyName] ? inputs[propertyName] : [];
views[propertyName] = views[propertyName] ? views[propertyName] : [];
views[propertyName].push(all[i]);
}
}
// 更新View
function updateView(propertyName) {console.log('update ' + propertyName);
if(views[propertyName]){
for(var i = 0,l = views[propertyName].length;i < l;i++){
views[propertyName][i].innerText = model[propertyName];
}
}
if(inputs[propertyName]){
for(var i = 0,l = inputs[propertyName].length;i < l;i++){
inputs[propertyName][i].value = model[propertyName];
}
}
}
function reset() {
for(var name in model){
model[name] = '';
}
}
</script>
</body>
</html>
点击右上角的“购买证书”按钮

找到免费的证书,阿里云的免费证书藏的比较深

选择品牌“Symantec”
证书类型里才会出现免费的,选择“免费型DV SSL”
购买并支付
打开证书管理页面
在证书列表里点击“补全”

填写域名信息
填写个人信息
提交
审核完成之后就可以在证书管理页面下载证书了

点击下载之后会打开下载证书的页面,选择对应的服务器的证书下载即可

在Nginx的安装目录下创建cert目录,并且将下载的全部文件拷贝到cert目录中。如果申请证书时是自己创建的CSR文件,请将对应的私钥文件放到cert目录下并且命名为214499409770626.key;
在/etc/nginx/sites-available里创建一个新的网站配置文件(以下属性中ssl开头的属性与证书配置有直接关系,其它属性请结合自己的实际情况复制或调整)
server {
listen 443;
server_name [你的域名];
ssl on;
root /var/www/html;
index index.php index.html index.htm index.nginx-debian.html;
ssl_certificate cert/[你的证书名字].pem;
ssl_certificate_key cert/[你的证书名字].key;
ssl_session_timeout 5m;
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_prefer_server_ciphers on;
location / {
root html;
index index.html index.htm;
}
}
创建一个连接,把你刚才编辑的文件连接到/etc/nginx/sites-enabled目录下
重启 Nginx
通过 https 方式访问您的站点,测试站点证书的安装配置。
首先需要注册一个小程序的账号,然后才能创建微信小程序。
打开https://mp.weixin.qq.com/wxopen/waregister?action=step1,填写表单,提交成功并且验证邮件之后你就拥有了小程序的账号,可以创建小程序了。
注意:这里的邮箱不能是公众号的邮箱。
打开https://mp.weixin.qq.com/wxopen/initprofile,点击第一步最右边的连接,填写小程序的信息,就能创建小程序了。
打开https://mp.weixin.qq.com/debug/wxadoc/dev/devtools/download.html, 下载对应操作系统的开发工具。下载完成之后安装。

项目目录选择一个空的文件夹,app id填写你创建的app的id
输入项目名称
选择模板,我这里选择的是腾讯云Node.js模版
点击确定,即可使用模板创建一个小程序。

这个时候小程序可以运行了。
我们可以通过上传一个体验版,来测试手机上的效果
点击上传:

提示上传成功之后,点击预览即可生成一个二维码,用微信扫一扫就能看到体验版的小程序。

注意:这个二维码只有https://mp.weixin.qq.com/wxopen/authprofile这个页面添加过的成员,并且有体验者权限的用户才能打开预览。
效果如下:

选择“智能上传”并且勾选“部署后自动安装依赖”

等待部署完成

点击测试登录接口

说明我们的后端代码部署成功了。
这样就创建了一个最简单的小程序。
git resetgit add过的文件
git checkout .
撤回没提交的更改,要在repo的根目录运行
git reset --hard HEAD
也可以撤回没提交的更改,可以在任意的子目录中运行
git clean -fdx
删除untracked文件
git reset --hard origin/master
回到过去

react-native init rndd
./app/components/Draggable.jsimport React, {
Component,
} from 'react';
import {
StyleSheet,
Image,
PanResponder,
Animated,
} from 'react-native';
class Draggable extends Component{
constructor(props){
super(props);
}
render(){
return (
<Animated.View style={styles.container}>
<Image style={{width:80,height:80}} source={require('../assets/react-native.jpg')}/>
</Animated.View>
)
}
}
export default Draggable;
const styles = StyleSheet.create({
container: {
position: 'absolute',
left: 100,
top: 100,
}
});
PanResponder的api可以上官网查看PanResponder
componentWillMount() {
this._panResponder = PanResponder.create({
onMoveShouldSetResponderCapture: () => true,
onMoveShouldSetPanResponderCapture: () => true,
onPanResponderGrant: (e, gestureState) => {
},
onPanResponderMove: Animated.event([
]),
onPanResponderRelease: (e, {vx, vy}) => {
}
});
}
render(){
return (
<Animated.View style={styles.container} {...this._panResponder.panHandlers}>
<Image style={{width:80,height:80}} source={require('../assets/react-native.jpg')}/>
</Animated.View>
)
}
先在state中创建一个对象来记录拖拽的记录。
constructor(props) {
super(props);
this.state = {
pan: new Animated.ValueXY()
};
}
更新panHandler:
componentWillMount() {
this._panResponder = PanResponder.create({
onMoveShouldSetResponderCapture: () => true,
onMoveShouldSetPanResponderCapture: () => true,
// 设置初始位置
onPanResponderGrant: (e, gestureState) => {
this.state.pan.setValue({x: 0, y: 0});
},
// 使用拖拽的偏移量来定位
onPanResponderMove: Animated.event([
null, {dx: this.state.pan.x, dy: this.state.pan.y},
]),
onPanResponderRelease: (e, {vx, vy}) => {
}
});
}
更新render方法
render(){
// 从state中取出pan
const { pan } = this.state;
// 从pan里计算出偏移量
const [translateX, translateY] = [pan.x, pan.y];
// 设置transform为偏移量
const imageStyle = {transform: [{translateX}, {translateY}]};
return (
<Animated.View style={[styles.container,imageStyle]} {...this._panResponder.panHandlers}>
<Image style={{width:80,height:80}} source={require('../assets/react-native.jpg')}/>
</Animated.View>
)
}
这个时候我们刷新用CMD+R刷新,发现已经可以拖动这个图片了,但是有个问题,第二次拖拽的时候又从原点开始移动,我们接下来解决这个问题。
我们有几个选择:
onPanResponderRelease: (e, {vx, vy}) => {
Animated.spring(
this.state.pan,
{toValue: {x: 0, y: 0}}
).start();
}
this._panResponder = PanResponder.create({
onMoveShouldSetResponderCapture: () => true,
onMoveShouldSetPanResponderCapture: () => true,
// 设置初始位置
onPanResponderGrant: (e, gestureState) => {
this.state.pan.setOffset({
x: this.state.pan.x._value,
y: this.state.pan.y._value
});
this.state.pan.setValue({x: 0, y: 0});
},
// 使用拖拽的偏移量来定位
onPanResponderMove: Animated.event([
null, {dx: this.state.pan.x, dy: this.state.pan.y},
]),
onPanResponderRelease: (e, {vx, vy}) => {
this.state.pan.flattenOffset();
}
});
先在state里加上一个scale用来记录放大的倍数
this.state = {
pan: new Animated.ValueXY(),
scale: new Animated.Value(1)
};
我们要在render里使用放大的值
render(){
// 从state中取出pan和scale
const { pan, scale } = this.state;
// 从pan里计算出偏移量
const [translateX, translateY] = [pan.x, pan.y];
// 设置transform为偏移量
const imageStyle = {transform: [{translateX}, {translateY}, {scale}]};
return (
<Animated.View style={[styles.container,imageStyle]} {...this._panResponder.panHandlers}>
<Image style={{width:80,height:80}} source={require('../assets/react-native.jpg')}/>
</Animated.View>
)
}
在拖拽的时候变大:
onPanResponderGrant: (e, gestureState) => {
this.state.pan.setOffset({
x: this.state.pan.x._value,
y: this.state.pan.y._value
});
this.state.pan.setValue({x: 0, y: 0});
Animated.spring(
this.state.scale,
{ toValue: 1.3, friction: 3 }
).start();
},
在释放的时候缩小到原来的样子:
onPanResponderRelease: (e, {vx, vy}) => {
this.state.pan.flattenOffset();
Animated.spring(
this.state.scale,
{ toValue: 1, friction: 3 }
).start();
}
先在state中加上一个rotate用来记录旋转的角度
this.state = {
pan: new Animated.ValueXY(),
scale: new Animated.Value(1),
rotate: new Animated.Value(0)
};
在拖拽的时候旋转,需要在onPanResponderGrant里加上
Animated.timing(this.state.rotate, {
toValue: 25, // 旋转25%,render里interpolate出deg的值
duration: 300
}).start();
在释放的时候恢复原状,需要在onPanResponderRelease中加上
Animated.timing(this.state.rotate, {
toValue: 0,
duration: 300
}).start();
render函数里设置transform:
render(){
// 从state中取出pan
const { pan, scale } = this.state;
// 从pan里计算出偏移量
const [translateX, translateY] = [pan.x, pan.y];
// 计算旋转
const rotate = this.state.rotate.interpolate({
inputRange: [0, 100],
outputRange: ['0deg', '360deg']
});
// 设置transform为偏移量
const imageStyle = {transform: [{translateX}, {translateY}, {scale}, {rotate}]};
return (
<Animated.View style={[styles.container,imageStyle]} {...this._panResponder.panHandlers}>
<Image style={{width:80,height:80}} source={require('../assets/react-native.jpg')}/>
</Animated.View>
)
}
https://github.com/shengoo/rndd
import React, {
Component,
} from 'react';
import {
StyleSheet,
Image,
PanResponder,
Animated,
} from 'react-native';
class Draggable extends Component{
constructor(props){
super(props);
this.state = {
pan: new Animated.ValueXY(),
scale: new Animated.Value(1),
rotate: new Animated.Value(0)
};
}
componentWillMount() {
this._panResponder = PanResponder.create({
onMoveShouldSetResponderCapture: () => true,
onMoveShouldSetPanResponderCapture: () => true,
// 设置初始位置
onPanResponderGrant: (e, gestureState) => {
this.state.pan.setOffset({
x: this.state.pan.x._value,
y: this.state.pan.y._value
});
this.state.pan.setValue({x: 0, y: 0});
Animated.spring(this.state.scale, {
toValue: 1.3,
friction: 3 }
).start();
Animated.timing(this.state.rotate, {
toValue: 25,
duration: 300
}).start();
},
// 使用拖拽的偏移量来定位
onPanResponderMove: Animated.event([
null, {dx: this.state.pan.x, dy: this.state.pan.y},
]),
onPanResponderRelease: (e, {vx, vy}) => {
this.state.pan.flattenOffset();
// Animated.spring(
// this.state.pan,
// {toValue: {x: 0, y: 0}}
// ).start();
Animated.spring(
this.state.scale,
{ toValue: 1, friction: 3 }
).start();
Animated.timing(this.state.rotate, {
toValue: 0,
duration: 300
}).start();
}
});
}
render(){
// 从state中取出pan
const { pan, scale } = this.state;
// 从pan里计算出偏移量
const [translateX, translateY] = [pan.x, pan.y];
// 计算旋转
const rotate = this.state.rotate.interpolate({
inputRange: [0, 100],
outputRange: ['0deg', '360deg']
});
// 设置transform为偏移量
const imageStyle = {transform: [{translateX}, {translateY}, {scale}, {rotate}]};
return (
<Animated.View style={[styles.container,imageStyle]} {...this._panResponder.panHandlers}>
<Image style={{width:80,height:80}} source={require('../assets/react-native.jpg')}/>
</Animated.View>
)
}
}
export default Draggable;
const styles = StyleSheet.create({
container: {
position: 'absolute',
left: 100,
top: 100,
}
});
n to manage your Node.js versionsnpm:(Recommended)npm install -g n
Since n‘s default path is /usr/local/n, it will need super user’s permission to modify file systems in the default path, we need to config n‘s path to user’s path.
N_PREFIXvim ~/.bashrc
export N_PREFIX=~/.n
source ~/.bashrc
vim ~/.bashrc
export PATH=$HOME/.n/bin:$PATH
source ~/.bashrc
# Use or install a version of node
n 9.0.0
# Use or install the latest official release
n latest
# Use or install the stable official release
n stable
# Use or install the latest LTS official release
n lts
Type n to show list of versions.
And select a version by up down button.
$ n
node/8.9.3
node/9.0.0
node/9.2.1
ο node/9.3.0
React的思想里,UI=render(data)
所以,React的组件一般是完成两种功能:
如果我们把两个功能都放在同一个组件内,那么这个组件做的事情就太多了,考虑到组件复用和业务变更,让一个组件只专注做一件事,我们可以把这个组件拆分成多个组件,让每个组件只专注做一件事,所以就衍生了一种React常用的模式:容器组件和展示组件。
容器组件在外层,有以下特点:
展示组件在容器组件内部,有以下特点:
展示组件一般可以使用纯函数的声明方式:
export default (props)=>(
<div>Hello {props.name}</div>
);
运行一下看看效果

新建一个react-native项目,和iOS的项目中同一个目录下
brew install node
brew install watchman
npm install -g react-native-cli
react-native init ReactNativeProject
react-native start
brew install cocoapods
pod init
# Uncomment the next line to define a global platform for your project
# platform :ios, '9.0'
target 'demo' do
# Comment the next line if you're not using Swift and don't want to use dynamic frameworks
use_frameworks!
# Pods for demo
pod 'React', :path => ‘../ReactNativeProject/node_modules/react-native', :subspecs => [
'Core',
'CxxBridge', # Include this for RN >= 0.47
'DevSupport', # Include this to enable In-App Devmenu if RN >= 0.43
'RCTText',
'RCTNetwork',
'RCTWebSocket', # needed for debugging
# Add any other subspecs you want to use in your project
]
pod "yoga", :path => "../ReactNativeProject/node_modules/react-native/ReactCommon/yoga"
pod 'DoubleConversion', :podspec => '../ReactNativeProject/node_modules/react-native/third-party-podspecs/DoubleConversion.podspec'
pod 'GLog', :podspec => '../ReactNativeProject/node_modules/react-native/third-party-podspecs/GLog.podspec'
pod 'Folly', :podspec => '../ReactNativeProject/node_modules/react-native/third-party-podspecs/Folly.podspec'
target 'demoTests' do
inherit! :search_paths
# Pods for testing
end
target 'demoUITests' do
inherit! :search_paths
# Pods for testing
end
end
pod install安装依赖,得到以下输出:
$ pod install
Setting up CocoaPods master repo
$ /usr/bin/git clone https://github.com/CocoaPods/Specs.git master --progress
Cloning into 'master'...
remote: Counting objects: 1799117, done.
remote: Compressing objects: 100% (377/377), done.
remote: Total 1799117 (delta 157), reused 35 (delta 35), pack-reused 1798692
Receiving objects: 100% (1799117/1799117), 500.73 MiB | 320.00 KiB/s, done.
Resolving deltas: 100% (981561/981561), done.
Checking out files: 100% (203691/203691), done.
Setup completed
Analyzing dependencies
Fetching podspec for `DoubleConversion` from `../ReactNativeProject/node_modules/react-native/third-party-podspecs/DoubleConversion.podspec`
Fetching podspec for `Folly` from `../ReactNativeProject/node_modules/react-native/third-party-podspecs/Folly.podspec`
Fetching podspec for `GLog` from `../ReactNativeProject/node_modules/react-native/third-party-podspecs/GLog.podspec`
Fetching podspec for `React` from `../ReactNativeProject/node_modules/react-native`
Fetching podspec for `yoga` from `../ReactNativeProject/node_modules/react-native/ReactCommon/yoga`
Downloading dependencies
Installing DoubleConversion (1.1.5)
Installing Folly (2016.09.26.00)
Installing GLog (0.3.4)
Installing React (0.51.0)
Installing boost (1.59.0)
Installing yoga (0.51.0.React)
Generating Pods project
Integrating client project
在AppDelegate.swift文件中引入React
import React
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
var tab = self.window?.rootViewController as! UITabBarController
let jsCodeLocation = URL(string: "http://localhost:8081/index.bundle?platform=ios")
let rootView = RCTRootView(
bundleURL: jsCodeLocation,
moduleName: "ReactNativeProject",
initialProperties: nil,
launchOptions: nil
)
let vc = UIViewController()
vc.view = rootView
vc.title = "rn"
tabbar.viewControllers?.append(vc)
return true
}
运行看看效果

style-loader adds CSS to the DOM by injecting a style tag.
The css-loader interprets @import and url() like import/require() and will resolve them.
Use it after css-loader and style-loader, but before other preprocessor loaders like e.g sass|less|stylus-loader, if you use any.
Autoprefixer is a service for managing vendor prefixes. It adds missing prefixes and deletes obsolete ones.
Extract text from a bundle, or bundles, into a separate file.
Needed in production mode.
import from './file.css'
import from './file.less'
import from './file.sass'
module.exports = {
module: {
rules: [
{
test: /\.css$/,
use: [
'style-loader',
'css-loader',
{
loader: 'postcss-loader',
options: {
plugins: [
autoprefixer({
browsers: ['> 5%']
})
]
}
}
]
},
{
test: /\.less$/,
use: [
'style-loader',
'css-loader',
{
loader: 'postcss-loader',
options: {
plugins: [
autoprefixer({
browsers: ['> 5%']
})
]
}
},
'less-loader'
]
}
]
}
}
module.exports = {
module: {
rules: [
{
test: /\.css$/,
use: ExtractTextPlugin.extract({
fallback: 'style-loader',
use: [
{
loader: 'css-loader',
options: {
importLoaders: 1,
minimize: true,
sourceMap: true,
},
},
{
loader: 'postcss-loader',
options: {
plugins: [
autoprefixer({
browsers: ['> 5%']
})
]
}
},
]
})
},
{
test: /\.less$/,
use: ExtractTextPlugin.extract({
fallback: 'style-loader',
use: [
{
loader: 'css-loader',
options: {
importLoaders: 1,
minimize: true,
sourceMap: true,
},
},
{
loader: 'postcss-loader',
options: {
plugins: [
autoprefixer({
browsers: ['> 5%']
})
]
}
},
'less-loader']
})
},
]
},
plugins: [
// Note: this won't work without ExtractTextPlugin.extract(..) in `loaders`.
new ExtractTextPlugin({filename: '[name].[hash:8].css'})
],
}