创建本地仓库
创建.gitignore配置文件
git init
git add *
git commit -m "xxx"
创建远程仓库
New Repository
指定名称
创建
将本地仓库推送到远程仓库
git remote add origin https://github.com/zxfjd3g/xxx.git 关联远程仓库
git push origin master
如果本地有更新, 推送到远程
git add *
git commit -m "xxx"
git push origin master
如果远程有更新, 拉取到本地
git pull origin master
git fetch origin master:tmp
克隆远程仓库到本地
git clone https://github.com/zxfjd3g/xxx.git
git checkout -b dev origin/dev
分支是在开发主线之外编写你的代码完成特定工作而不影响开发主线
分支的操作:
查看分支: git branch
创建分支: git branch dev
切换分支: git checkout dev
比较分支: git diff master dev
合并分支: git merge dev
冲突
产生冲突: 当对2个分支进行合并时, 如果同一个文件的同一行内容不一样, 就会产生冲突
解决冲突:
修正合并后产生冲突的代码
git add *
git commit -m "resolve conflict"
master分支
主分支,产品的功能全部实现后,最终在master分支对外发布。
develop分支
开发分支,基于master分支克隆,产品的编码工作在此分支进行。
release分支
测试分支,基于delevop分支克隆,产品编码工作完成后,发布到本分支测试,
测试过程中发现的小bug直接在本分支进行修复,修复完成后合并到develop分支。
本分支属于临时分支,目的实现后可删除分支。
bugfix/hotfix分支
Bug修复分支,基于master分支或发布的里程碑Tag克隆,
主要用于修复对外发布的分支,收到客户的Bug反馈后,
在此分支进行修复,修复完毕后分别合并到develop分支和master分支。
本分支属于临时分支,目的实现后可删除分支。
feature分支
功能特征分支,基于develop分支克隆,
主要用于多人协助开发场景或探索性功能验证场景,功能开发完毕后合并到develop分支。
feature分支可创建多个,属于临时分支,目的实现后可删除分支。
git clone: 克隆远程仓库到本地
git remote: 添加/删除远程仓库关联
git push: 将本地仓库推送到远程仓库
git pull: 从远程仓库更新拉取到本地仓库并与当前分支合并
git fetch: 从远程仓库更新拉取到本地仓库的临时新分支
当工业级的项目开发的足够大的时候,如果将所有的js代码定义在一个js文件的话,使得复杂度提升,后期维护难度加大。
如果将一个大的js文件根据一定的规范拆分成几个小的文件的话将会便于管理,可以提高复用性。
模块化在项目中十分的重要,一个复杂的项目肯定有很多相似的功能模块,如果每次都需要重新编写模块肯定既费时又耗力。但是引用别人编写模块的前提是要有统一的“打开姿势”,如果每个人有各自的写法,那么肯定会乱套,所有会引出模块化规范的使用
常用的JavaScript模块化规范有四种:
Commonjs, AMD(require.js), CMD(sea.js), ES6模块化
CommonJS是服务器端模块的规范,Node.js采用了这个规范。但目前也可用于浏览器端,需要使用Browserify/webpack进行提前编译打包。
ES6模块化规范专门针对于浏览器端,但目前浏览器支持不是很好(只有Chrome浏览器支持), 也需要使用Browserify进行打包。
暴露的方式和暴露的本质:
①commonjs暴露的方式
②ES6中暴露的方式
构建项目到底做些什么
编译项目中的js, sass, less, stylus
合并js/css等资源文件
压缩js/css/html等资源文件
JS语法的检查
构建工具
作用: 简化项目构建, 实现自动化构建
常用: grunt/gulp/webpack
module.exports = {
entry: '',
output: {},
module: {rules: []},
plugins: []
}
(1) 连接: webpack从入口JS开始, 递归查找出所有相关联的模块, 并连接
起来形成一个图(网)的结构
(2) 编译: 将JS模块中的模块化语法编译
为浏览器可以直接运行的模块语法(当然其它类型资源也会处理)
(3) 合并: 将图中所有编译过的模块合并
成一个或少量几个bundle文件, 而浏览器运行是打包生成的bundle文件
Webpack在打包js模块时, 会将模块中向外暴露但没有被使用的功能从打包文件中移除。
2个前提:
相同点: 代码修改后都会自动重新编译打包。
不同点:
定义变量/常量: const/let
解构赋值:
let {a, b} = this.props
import {aa} from 'xxx'
function f ({name}) {}
对象的简洁表达: {a, b, c () {}}
箭头函数:
扩展运算符: …
类: class/extends/constructor/super
ES6模块化: export/default/import
异步: promise, async/await
DOM事件
自定义事件
作用?
哪里使用await?
哪里使用async?
公司前后招聘了10个员工(性别,年龄, 月薪各不相同),有以下需求
1). 列表显示所有员工的所有信息 forEach
2). 对员工进行年薪降序列表显示 sort()
3). 得到男员工的总月薪: reduce()
4). 查找一个月薪只比12000高一点点的员工: find()
5). 查找出所有月薪高于12000的员工: filter()
6). 列表显示所有员工的姓名/性别和年薪: map()
const employees = [
{name: 'A', sex: '男', age: 21, salary: 10000},
{name: 'B', sex: '女', age: 25, salary: 12000},
{name: 'C', sex: '男', age: 24, salary: 13000},
{name: 'D', sex: '男', age: 24, salary: 12500},
{name: 'E', sex: '女', age: 21, salary: 14000},
{name: 'F', sex: '男', age: 24, salary: 16000},
{name: 'G', sex: '男', age: 23, salary: 9000},
{name: 'H', sex: '女', age: 21, salary: 11000},
{name: 'I', sex: '男', age: 23, salary: 13200},
{name: 'J', sex: '男', age: 23, salary: 15000}
]
employees.forEach(e => console.log(e))
employees.sort((e1, e2) => e2.salary-e1.salary)
employees.reduce((preTotal, e) => preTotal + (e.sex=='男'?e.salary:0), 0)
employees.find(e => e.salary>1200 && e.salary<1400 && e.sex==='男')
employees.filter(e => e.salary>1200)
employees.map(e => ({'姓名/性别': `${e.name}/${e.sex}`, '年薪': e.salary*12}))
概念:cookie是由key和value组成的文本小数据。
分类: 会话cookie和持久化cookie。
创建:由服务器端创建: res.cookie(key, value, {maxAge: 1000})
保存:由浏览器端保存,浏览器接收到新的cookie会自动保存(内存/文件)
使用: 浏览器发送请求时自动携带对应的cookie, 服务器端通过req读取: req.cookies.key
function Foo () {}
const fn1 = new Foo()
const fn2 = new Foo()
const o1 = {}
const o2 = new Object()
变量提升: 在变量语句前就可以读取到变量, 值为undefined。
函数提升: 在函数定义语句前就可以调用函数。
原因: JS引擎在运行全局代码或执行函数前有预处理/解析
作用: 原型链用于查找对象的属性。
是什么: 实例对象上都会有一个隐式原型属性(proto), 它指向的就是原型对象, 而原型对象也有__proto__属性指向它的原型对象,以此类推,形成构成了一个原型链。
为什么__proto__指向的是原型对象?
构造函数对象上有显式原型属性(prototype), 它指向的就是原型对象
实例对象的__proto__属性被赋值为构造函数的prototype属性值。
作用: 作用链用来查找变量。
是什么: 多个由内向外作用域形成的链。
作用域: 一块代码区域。
分类:全局作用域和函数/局部作用域, ES6有了块作用域
function fn1 () {
var a = 2
function fn2 () {
a++
console.log(a)
}
return fn2
}
var f = fn1()
f()
f()
f = null
调试的目的
1)查找bug: 不断缩小可疑代码的范围
2)查看程序的运行流程(用于熟悉新接手项目的代码)
如何开启调试模式
1)添加语debugger句: 程序运行前 此方式用打包后才运行的项目
2)添加(打)断点: 程序运行前或者过程中 此方式用运行源码js
如何进行调试操作
resume: 恢复程序执行(可能执行完或者进入下一个断点处)
step ove: 单步跳转, 尝试执行完当前语句, 进入下一条(如果内部有断点, 自动进入内部断点处)
step into: 跳入, 进入当前调用函数内部
step out: 跳出, 一次性执行完当前函数后面所有语句,并出去
deactivate breakpoints: 使所有断点暂时失效
call stack: 显示是程序函数调用的过程
scop: 当前执行环境对应的作用域中包含的变量数据
breakpoints: 断点列表
什么函数才是回调函数
你定义的
你没有直接调用
它最终执行了
回调函数相关的3个问题
什么时候执行
用来做什么的
函数中的this是谁
正常情况: 执行函数的方式决定了函数中的this
特别情况:
在开发我们经常会利用箭头函数/bind()来改变this的指向
localStorage: 浏览器端持久化存储, 关闭浏览还存在, 最大5MB(基本没限制了)。
sessionStorage: 浏览器端内存存储, 关闭浏览器不存在
session: 服务器端创建, 服务器端保存, 依赖于cookie
cookie: 服务器端创建, 浏览器端保存, 请求携带对应cookie, 长度和数量有限制(4kb)
先查找a, 沿着作用域链查找, 找不到报错(变量未定义)
找到后查找对象上的b属性, 查找原型链, 如果找不到返回undefined
let a = {age: 1}
let b = a
a.age = 2 // 操作a也会影响b
console.log(b.age) // 2
从上述例子中我们可以发现,如果给一个变量赋值一个对象,那么两者的值会是同一个引用,其中一方改变,另一方也会相应改变。
function deepClone(target) {
// 1. 初始化变量,获取目标数据的类型
let result, targetClass = getTargetClass(target);
// 2. 判断目标的类型
if(targetClass === 'Object'){ // 对象
result = {};
}else if(targetClass === 'Array'){ // 数组
result = [];
}else {
return target;
}
// 3. 遍历目标数据
for(let key in target){
// 获取每一项值
let item = target[key];
// 4. 判断每一项数据的类型
if(getTargetClass(item) === 'Object' || getTargetClass(item) === 'Array'){
// 无论是对象还是数组,都可以result[key]取值
// item = {name: 'kobe'} item[]
result[key] = deepClone(item);
}else {
result[key] = item;
}
}
return result;
}
//定义一个判断数据类型的函数
function getTargetClass(target) {
return Object.prototype.toString.call(target).slice(8, -1)
}
1.前端写法
<body>
<button id="btn">按钮</button>
<script type="text/javascript">
var btn = document.getElementById('btn');
btn.onclick = function () {
//1. 创建一个script标签
var script = document.createElement('script');
//2. 设置回调函数
window.getData = function (data) {
console.log(data);//拿到数据
}
//3. 设置script标签src属性,填写跨域请求的地址
script.src = 'http://localhost:3000/jsonp?callback=getData';
//4. 将script标签添加到body中生效
document.body.appendChild(script);
//5.不影响整体DOM结构,删除script标签
document.body.removeChild(script);
}
</script>
</body>
2.后端写法
app.get('/jsonp', (req, res) => {
//解构赋值获取请求参数
const {callback} = req.query
//去数据库查找对应数据
const data = [{name: 'tom', age: 18}, {name: 'jerry', age: 20}];
res.send(callback + '(' + JSON.stringify(data) + ')');
})
以Node为例:
res.set(‘Access-Control-Allow-Origin’, ‘http://localhost:63342’);//存在安全问题
// 以上两种凡是解决跨域, 第三种使用代理工具 反向代理工具
// 防抖(一段时间会等,然后带着一起做了)
function debounce(fn, delay){
let timerId = null
return function(){
const context = this
if(timerId){window.clearTimeout(timerId)}
timerId = setTimeout(()=>{
fn.apply(context, arguments)
timerId = null
},delay)
}
}
// 节流(一段时间执行一次之后,就不执行第二次)
function throttle(fn, delay){
let canUse = true
return function(){
if(canUse){
fn.apply(this, arguments)
canUse = false
setTimeout(()=>canUse = true, delay)
}
}
}
var request = new XMLHttpRequest()
request.open('GET', '/a/b/c?name=ff', true);
request.onreadystatechange = function () {
if(request.readyState === 4 && request.status === 200) {
console.log(request.responseText);
}};
request.send();
function MyCom1(props) {
return <h1>自定义组件标题11111</h1>
}
class MyCom2 extends React.Component {
render () { // 回调函数, 为组件对象提供虚拟DOM
return <h1>自定义组件标题33333</h1>
}
}
ReactDOM.render(<MyComponent1/>, domContainer)
编码的3步
拆分组件
实现静态组件
实现动态组件
a. 动态显示初始化数据
b. 交互
2个重要问题
一、初始化阶段:ReactDOM.render(, containDom)
二、更新阶段:this.setState({})
三、销毁阶段:ReactDOM.unmountComponentAtNode(div)
方式一: 通过props传递
通过props可以传递一般数据和函数数据,
一般数据–>父组件向子组件
函数数据–>子组件向父组件通信
缺点: 只能一层一层传递/兄弟组件必须借助父组件
方式二: 使用消息订阅(subscribe)-发布(publish)机制
实现库: pubsub-js
组件A: 发布消息(相当于触发事件)Pubsub.publish("事件","数据")
组件B: 订阅消息, 接收消息, 处理消息(相当于绑定事件监听)Pubsub.subscribe("事件",(事件,数据)=>{})
优点: 对组件关系没有限制
写在componentDidMount
中
方式三: redux
通过redux可以实现任意组件间的通信
集中式管理多个组件共享的状态, 而pubsub-js并不是集中式的管理
跨组件传值——context
context上下文对象,无需为每一层徐建手动添加props,就能在组件数据之间进行数据传递的方法。
使用createContext()方法提供了两个对象:
Provider对象
——生产者,用来生产数据;
Consumer对象
——消费者,用来使用数据;
步骤:
①创建一个上下问对象的文件,并且设置{this.props.children}
②在根里面引用相关的上下文对象组件 并使用
③创建上下文对象
// 创建上下文对象数据
let context=createContext()
let {Provider,Consumer}=context
{/* 1.添加this.props.children */}
<Provider value={123}>
{this.props.children}
</Provider>
④修改根中的组件引用
⑤在组件中使用
我是爷爷组件
<Consumer>
{
(value)=>{
return (
<span>{value}</span>
)
}
}
</Consumer>
下载: npm install --save react-router-dom
是什么: 用来实现SPA的react插件
相关API:
a. 组件
/
/
b. 对象或函数
props.history对象
props.match对象
props.location对象
withRouter函数
redux是一个独立专门用于做状态管理的JS库(不是react插件库)
可以与任何前端库配合使用, 但最合适的是与react库配合
作用: 集中式管理react应用中多个组件共享的状态
提供的API:
createStore()
applyMiddleWare()
combineReducers()
store.getState()/dispatch()/subscribe()
store.js
reducers.js
actions.js
action-types.js
<Provider store={store}>
export default connect(
state => ({xxx: state.xxx}),
{action1, action2}
)(UI组件)
shouldComponentUpdate 这个方法用来判断是否需要调用render方法
类似问题: 为什么列表的key尽量不要用index
key
的作用?受控组件
(controlled component)组件中的表单项能在输入过程中自动收集输入数据到状态中
在react中必须通过onChange
监听才能实现
高阶组件是一个以组件为参数并返回一个新组件的函数。
HOC 运行你重用代码、逻辑和引导抽象。
最常见的可能是Redux 的 connect 函数.
如果你发现你在不同的地方写了大量代码来做同一件事时,就应该考虑将代码重构为可重用的 HOC
redux
是用来对组件的状态数据进行集中式管理的框架。action,store,reducer
dispatch()
分发action,reducer
执行, 返回新的state
view
通过store提供的getState()
获取最新的数据何为 redux
redux 的基本思想是应用的state保存在一个单一的store对象中。
而改变应用state的唯一方式是在应用中触发actions,然后为这些actions编写reducers来修改state。
整个 state 转化是在 reducers 中完成,并且不应该有任何副作用
在 Redux 中,何为 store
Store 是一个 javascript 对象,它保存了整个应用的 state。与此同时,Store 也承担以下职责:
允许通过 getState() 访问 state
通过 dispatch(action) 改变 state
通过 subscribe(listener) 注册 listeners
何为 action
action是一个纯 js对象,必须有一个 type 属性表明正在执行的 action 的类型。
实质上,action 是将数据从应用程序发送到 store 的有效载荷。
何为 reducer
一个reducer是一个纯函数,该函数以先前的state和一个action作为参数,并返回新的state。
**redux-thunk
**的作用是什么
redux-thunk是一个允许你编写返回一个函数而不是一个 action对象的中间件。
thunk可以用来延迟 action 的派发(dispatch),也就是处理异步 action 的派发(dispatch)。
何为纯函数(pure function)
一个纯函数是一个不依赖于且不改变其作用域之外的变量状态的函数,
这也意味着一个纯函数对于同样的参数总是返回同样的结果。
vue是一个前端JS库
MVVM模式的实现
作用: 动态构建用户界面
重要技术: 模板与数据绑定(angular), 组件与虚拟DOM(react)
M: Model(模型), vue中是data(为view提供数据)
V: View(视图), vue中是模板页面(显示data中的数据)
VM: ViewModel(视图模型), vue中是Vue实例对象(管理者: 数据绑定/DOM监听)
html中嵌套js
指令: < p v-text='msg'>
大括号表达式: {{msg}}
el: 最外层元素选择器
props: 声明接收哪些属性
data: 状态数据
computed: 计算属性
methods: 事件回调函数
watch: 监视属性变化
directives: 注册局部指令
filters: 注册局部过滤器
components: 配置组件
什么时候用计算属性?
模板显示要显示的数据是根据n个已有的相关数据进行计算来确定
v-text: 设置标签体文本
v-html: 设置标签体子标签
v-if/v-else/v-show: 显示/隐藏
v-for: 遍历显示列表
v-bind: 强制绑定表达式, 简写:
v-on: 绑定事件监听, 简写@
v-model: 双向数据绑定
vue-resource:
vue的插件, 用于vue1.xaxios:
独立的第三方库, 用于vue2.x
VueRouter:
路由构建函数, 用于创建路由器对象, 配置路由$route:
代表当前路由的对象, 包含当前路由相关信息(path, params参数, query参数)$router:
代表路由器对象, 包含控制路由跳转的方法(push/replce/back())vue的插件
对vue应用中多个组件的共享状态进行集中式的管理(读/写)
下载vuex
创建store对象
export default new Vuex.Store({
state,
mutations,
actions,
gettters
})
配置store对象
new Vue({ store })
组件中与vuex通信
$store.state/mapState()
$store.getters/mapGetters()
$store.dispatch()/mapActions()
是什么?
通过vm对象来代理data对象中所有属性的操作
作用:
简化操作vm中的data对象中的数据
实现基本原理
Object.defineProperty()
给vm添加与data对象的属性对应的属性描述符getter/setter
getter/setter
内部去操作data中对应的属性数据fragment
对象中fragment
中的所有层次子节点递归进行编译解析处理fragment
添加到el中显示textNode.textContent = value
elementNode.addEventListener('eventName', callback)
elementNode.xxx = value
Object.defineProperterty()
给data中所有属性添加setter/getter
, 实现数据劫持data
中的属性创建一个对应的dep对象
, 一旦属性数据变化, 通知dep对象
watcher
, 并关联到对应的dep上Object.defineProperty()来劫持/监视data中的属性
,在数据变动时发布消息给所有订阅者,每个订阅者去更新对应的DOM节点。给元素绑定input监听
, 一旦输入改变了, 将最新的值保存到对应的属性上data
props
computed: 根据data/props/其它compute属性/vuex的state或getters计算产生
scroll = new BScroll(selector, {})
scroll.on(eventName, cb)
scroll.scrollTo()
scroll.scrollToElement()
scroll.refresh()
创建Swiper/BScroll对象
后, 轮播/滑动没有效果?
原因: 创建对象太早, 得在数据显示后创建
解决: watch + $nextTick() 或 callback + $nextTick()
初始显示异常
Cannot read property 'xxx' of undefined"
v-if指令
Cannot read property 'avatar' of null"
初始值为{}
问题: 更新状态数据, 对应的界面不变化
原因: 一般方法给一个已有绑定的对象中添加一个新的属性, 这个属性没有数据绑定
解决:
Vue.set(obj, 'xxx', value)
才有数据绑定
this.$set(obj, 'xxx', value)
才有数据绑定
问题: 点击添加购物项, 会1次添加多个
原因: 创建了多个BScroll对象来管理同一个DOM元素
解决: 只创建一个BScroll对象
扩展: 单例对象:
创建前, 先判断是否已经存在, 只有不存在才创建
创建后, 保存创建的对象
ajax
ajax请求函数
接口请求函数
vuex
store
state
mutation-types
mutations
actions
getters
组件
dispatch(): 异步获取后台数据到vuex的state
mapState()/mapGetters(): 从vuex的state中读取对应的数据
模板中显示
如果频繁切换使用v-show比较合适
一旦涉及到初始化模板显示3层表达式数据, 使用v-if可以解决问题
$route
是“路由信息对象”: 包括path,params,hash,query,meta等路由信息参数。
$router
是“路由实例”对象: 包括了路由的跳转方法,注册全局导航守卫的方法。
但不会提交给服务器端, 只在浏览器端使用
没有#
, 利用HTML5的新特性: pushState()/replaceState()及popState事件监听路径会提交给后台
, 在生产环境下会出现404的问题{
"name": "react-demo", // 标识名称
"version": "1.0.0", // 版本号
"scripts": { // 打包运行相关的npm命令
"xxx": "具体命令" npm run xxx
},
dependencies: {}, // 运行时依赖
devDependencies: {} // 开发时依赖
npm start/ npm run dev
npm run build
serve build/dist
前后台分离
的招聘的SPA
, 包括前端应用和后端应用注册/登陆
, 大神/老板列表, 实时聊天等模块React全家桶+ES6+Webpack等技术
Node + express + mongodb + socketIO等技术
模块化
、组件化
、工程化
的模式开发exports
, exports的默认值为{}module.exports = value
exports.xxx = value1
exports.yyy = value2
const module = require('模块名/模块路径')
Elements: 查看DOM标签和样式
Console: 查看打印和错误信息
NetWork: 查看请求(url, 请求方式, 请求参数)和响应
Application: 查看浏览器端存储(localStorage, sessionStorage, cookie)
Sources: debugger调试
react: 查看react组件(state, props)
redux: 查看redux管理的state
vue: 查看vue组件或vuex状态