Taohong的博客


  • Home

  • Archives

ReactNative 热更新 PushCode

Posted on 2020-12-14 | In 技术

React Native Client SDK入门

安装插件

1
npm install --save react-native-code-push

Android

安装插件

  1. 在您的android/settings.gradle文件中,添加以下内容:
1
2
include ':app', ':react-native-code-push'
project(':react-native-code-push').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-code-push/android/app')
  1. 在android/app/build.gradle文件中,将文件添加codepush.gradle为下面的其他构建任务定义react.gradle:
1
2
3
4
...
apply from: "../../node_modules/react-native/react.gradle"
apply from: "../../node_modules/react-native-code-push/android/codepush.gradle"
...
  1. MainApplication.java通过以下更改更新文件以使用CodePush:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
...
// 1. Import the plugin class.
import com.microsoft.codepush.react.CodePush;
public class MainApplication extends Application implements ReactApplication {
private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {
...
// 2. Override the getJSBundleFile method to let
// the CodePush runtime determine where to get the JS
// bundle location from on each app start
@Override
protected String getJSBundleFile() {
return CodePush.getJSBundleFile();
}
};
}
  1. 将部署密钥添加到strings.xml:

为了让CodePush运行时知道它应该查询哪些部署更新,请打开您的应用程序的strings.xml文件,并添加一个名为新的字符串CodePushDeploymentKey,要配置对这个应用程序(如为关键,它的值是部署的关键Staging部署的FooBar应用程序)。您可以通过appcenter codepush deployment list -a / -k在App Center CLI中运行来检索此值(该-k标志是必需的,因为默认情况下不会显示键),然后复制与Key您要使用的部署相对应的列的值(请参见下文)。使用部署名称(例如暂存)将不起作用。“友好名称”仅用于CLI中经过身份验证的管理用途,不适用于您的应用程序中的公共使用。

图片

为了有效利用与CodePush应用程序一起创建的Staging和Production部署,请在实际将您的应用程序对CodePush的使用移入生产之前,请参考下面的多部署测试文档。

react native 图标字体

Posted on 2020-08-21 | In 技术

https://developer.aliyun.com/mirror/npm/package/react-native-iconfont-cli

安装

1
yarn add react-native-svg

因为涉及到原生代码, 需要重新安装应用

1
2
3
4
5
6
7
# 重新安装 ios
cd ios
pod install
npm run ios

# 重新安装 andorid
npm run android
1
2
3
4
yarn add -D react-native-iconfont-cli

# 创建配置文件
npx iconfont-init

根据需求配置 iconfont.json

1
2
3
4
5
6
7
{
"symbol_url": "//at.alicdn.com/t/font_2018217_2h9xudzdl62.js",
"use_typescript": true,
"save_dir": "./src/components/iconfont",
"trim_icon_prefix": "",
"default_icon_size": 18
}

生成组件

1
npx iconfont-rn

react native 导航器

Posted on 2020-08-21 | In 技术

https://reactnavigation.org/

安装导航器

安装核心包

1
yarn add @react-navigation/native

安装其它依赖

1
yarn add react-native-reanimated react-native-gesture-handler react-native-screens react-native-safe-area-context @react-native-community/masked-view

处理原生内容
ios

1
npx pod-install ios

android

处理手势库

https://docs.swmansion.com/react-native-gesture-handler/docs/

MainActivity.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package com.swmansion.gesturehandler.react.example;

import com.facebook.react.ReactActivity;
+ import com.facebook.react.ReactActivityDelegate;
+ import com.facebook.react.ReactRootView;
+ import com.swmansion.gesturehandler.react.RNGestureHandlerEnabledRootView;
public class MainActivity extends ReactActivity {

@Override
protected String getMainComponentName() {
return "Example";
}
+ @Override
+ protected ReactActivityDelegate createReactActivityDelegate() {
+ return new ReactActivityDelegate(this, getMainComponentName()) {
+ @Override
+ protected ReactRootView createRootView() {
+ return new RNGestureHandlerEnabledRootView(MainActivity.this);
+ }
+ };
+ }
}

导入 index.js 或 App.js

1
import 'react-native-gesture-handler';

堆栈式导航器

https://reactnavigation.org/docs/hello-react-navigation

安装

1
yarn add @react-navigation/stack

使用

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
// In App.js in a new project

import * as React from 'react';
import { View, Text } from 'react-native';
import { NavigationContainer } from '@react-navigation/native';
import { createStackNavigator } from '@react-navigation/stack';

function HomeScreen() {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Home Screen</Text>
</View>
);
}

const Stack = createStackNavigator();

function App() {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen name="Home" component={HomeScreen} />
</Stack.Navigator>
</NavigationContainer>
);
}

export default App;

底部导航器

https://reactnavigation.org/docs/bottom-tab-navigator

安装

1
yarn add @react-navigation/bottom-tabs

使用

1
2
3
4
5
6
7
8
9
10
11
12
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';

const Tab = createBottomTabNavigator();

function MyTabs() {
return (
<Tab.Navigator>
<Tab.Screen name="Home" component={HomeScreen} />
<Tab.Screen name="Settings" component={SettingsScreen} />
</Tab.Navigator>
);
}

react native 项目初始化

Posted on 2020-08-21 | In 技术

初始化项目

1
2
3
4
5
npx react-native init projectname --template react-native-template-typescript

# 启动项目
yarn ios
yarn android

多环境

https://js.coach/package/react-native-config

1
yarn add react-native-config

根目录下新建 .env

1
2
API_URL=https://myapi.com
GOOGLE_MAPS_API_KEY=abcdefgh

使用

1
2
3
4
import Config from "react-native-config";

Config.API_URL; // 'https://myapi.com'
Config.GOOGLE_MAPS_API_KEY; // 'abcdefgh'

安装

ios

1
(cd ios; pod install)

android
android/app/build.gradle

1
2
// 2nd line, add a new apply:
apply from: project(':react-native-config').projectDir.getPath() + "/dotenv.gradle"

绝对路径

https://www.npmjs.com/package/babel-plugin-module-resolver

1
yarn add babel-plugin-module-resolver

配置 .babelrc 或 babel.config.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
module.exports = {
presets: ['module:metro-react-native-babel-preset'],
plugins: [
[
'module-resolver',
{
root: ['./src'],
alias: {
'@': './src',
},
},
],
],
};

配置 tsconfig.json

1
2
3
4
5
6
{
"baseUrl": "./src",
"paths": {
"@/*": ["*"],
},
}

前端面试总结

Posted on 2020-04-20 | In 技术

vue

v-show 和 v-if 的区别

  1. v-show 通过 css display 控制显示和隐藏
  2. v-if 组件真正的渲染和销毁, 而不是显示和隐藏
  3. 频繁切换显示状态用 v-show, 否则用 v-if

为何在 v-for 中用 key

  1. 必须用 key, 且不能是 index 和 random
  2. diff 算法中通过 tag 和 key 来判断, 是否是 sameNode
  3. 减少渲染次次数, 提升渲染性能

描述 vue 组件生命周期(父子组件)

  1. 单组件生命周期图
  2. 父子组件生命周期关系

vue 组件如何通讯(常见)

  1. 父子组件 props 和 this.$emit
  2. 自定义事件 event.$on event.$off event.$emit
  3. vuex

描述组件渲染和更新的过程

Alt text

双向数据绑定 v-model 的实现原理

  1. input 元素的 value = this.name
  2. 绑定 input 事件 this.name = $event.target.value
  3. data 更新触发 re-render

对 MVVM 的理解

Alt text

computed 有何特点

  1. 有缓存, data 不变不会重新计算
  2. 提高性能

为何组件中的 data 必须是一个函数

组件是可复用的 vue 实例, 如果组件被复用, data 又不是函数, data 会指向同一个对象, 会相互影响.

ajax 请求应该放在哪个生命周期

  1. mounted
  2. js 是单线程的, ajax 异步获取数据
  3. 放在 mounted 之前没有用, 只会让逻辑更加混乱

在created的时候,视图中的dom并没有被渲染出来,所以此时如果直接去操作dom节点,无法找到相关元素。
在mounted中,由于此时的dom元素已经渲染出来了,所以可以直接使用dom节点。
一般情况下,都放在mounted中,保证逻辑的统一性。因为生命周期是同步执行的,ajax是异步执行的。
服务端渲染不支持mounted方法,所以在服务端渲染的情况下统一放在created中。

如何将组件所有 props 传递给子组件?

  1. v-bind=”$props”

如何自己实现 v-model

一个组件上的 v-model 默认会利用名为 value 的 prop 和名为 input 的事件,也可以自定义, 受用 model 选项就可以了

多个组件有相同的逻辑, 如何抽离?

  • mixin
  • 以及 mixin 的一些缺点

何时使用异步组件

  1. 加载大组件
  2. 路由异步加载

何时需要使用 keep-alive

  1. 可以缓存组件, 不需要重新渲染
  2. 如多个静态 tab 页的切换
  3. 优化性能

何时需要使用 beforeDestory

  1. 解绑自定义事件 event.$off
  2. 清除定时器
  3. 解绑自定义的 DOM 事件, 如 window scroll 等

什么是作用域插槽

vuex 中 action 和 mutation 有何区别

  1. action 中处理异步, mutation 不可以
  2. mutation 做原子操作
  3. action 可以整合多个mutation

vue-router 常用的路由模式

  1. hash 默认
  2. H5 history(需要服务端支持)
  3. 两者比较

如何配置 vue-router 异步加载

使用 import() 函数

请用 vnode 描述一个 dom 结构

监听 data 变化的核心 api 是什么

  1. Object.defineProperty
  2. 以及监听深度, 监听数组
  3. 有何缺点

vue 如何监听数组变化

  1. Object.defineProperty 不能监听数组变化
  2. 重新定义原型, 重写 push pop 等方法, 实现监听
  3. Proxy 可以原生监听数组变化

请描述响应式原理

  1. 监听 data 变化
  2. 组件渲染和更新的流程

diff 算法的时间复杂度

  1. O(n)
  2. 在 O(n^3) 基础上做了一些调整

简述 diff 算法过程

  1. patch(elem, vnode) 和 patch(vnode, newVnode)
  2. patchVnode 和 addVnodes 和 remvoeVnodes
  3. updateChildren(key 的重要性)

vue 为何是异步渲染, $nextTick 何用?

  1. 异步渲染(以及合并 data 修改), 以提高渲染性能
  2. $nextTick 在 dom 更新完后, 触发回调

vue 常见性能优化方式

  1. 合理使用 v-show 和 v-if
  2. 合理使用 computed
  3. v-for 时加 key, 以及避免和 v-if 同时使用
  4. 自定义事件, dom 事件及时销毁
  5. 合理使用异步组件
  6. 合理使用 keep-alive
  7. data 层级不要太深
  8. 使用 vue-loader 在开发环境做模板编译(预编译)
  9. webpack 层面的优化
  10. 前端通用性能优化, 如懒加载
  11. 使用 SSR

webpack

webpack 常见配置

  1. 拆分配置和 merge
  2. 启动本地服务
  3. 处理 es6
  4. 处理样式
  5. 处理图片
  6. (模块化)

前端为何要进行打包和构建

  1. 体积更小(Tree-Shaking, 压缩, 合并), 加载更换
  2. 编译高级语言或者预发(TS, ES6+, 模块化, scss)
  3. 兼容性和错误检测(Polyfill, postcss, eslint)
  4. 统一, 高效的开发环境
  5. 统一的构建流程和产出标准
  6. 集成公司规范构建(提测, 上线等)

module chunk bundle 的区别

  1. module - 各个源码文件, webpack 中一切皆模块
  2. chunk - 多模块合并成的, 如 entry import() splitChunk
  3. bundle - 最终输出文件

loader 和 plugin 的区别

  1. loader 模块转换器, 如 less -> css
  2. plugin 扩展插件, 如 HtmlWebpackPlugin

Loader直译为”加载器”。Webpack将一切文件视为模块,但是webpack原生是只能解析js文件,如果想将其他文件也打包的话,就会用到loader。 所以Loader的作用是让webpack拥有了加载和解析非JavaScript文件的能力。
Plugin直译为”插件”。Plugin可以扩展webpack的功能,让webpack具有更多的灵活性。 在 Webpack 运行的生命周期中会广播出许多事件,Plugin 可以监听这些事件,在合适的时机通过 Webpack 提供的 API 改变输出结果。

常见的 loader 和 plugin 有哪些

查看文档, 结合自己的项目说

loader
处理图片的 url-loader

转换编译
babel-loader 加载 ES2015+ 代码,然后使用 Babel 转译为 ES5

处理样式的
style-loader 将模块的导出作为样式添加到 DOM 中
css-loader 解析 CSS 文件后,使用 import 加载,并且返回 CSS 代码
less-loader 加载和转译 LESS 文件
sass-loader 加载和转译 SASS/SCSS 文件
postcss-loader 使用 PostCSS 加载和转译 CSS/SSS 文件
stylus-loader 加载和转译 Stylus 文件

清理和测试
eslint-loader PreLoader,使用 ESLint 清理代码

框架
vue-loader 加载和转译 Vue 组件

plugin
CopyWebpackPlugin
将单个文件或整个目录复制到构建目录
DefinePlugin
允许在编译时(compile time)配置的全局常量

DllPlugin
为了极大减少构建时间,进行分离打包
EnvironmentPlugin
DefinePlugin 中 process.env 键的简写方式。
ExtractTextWebpackPlugin
从 bundle 中提取文本(CSS)到单独的文件
HotModuleReplacementPlugin
启用模块热替换(Enable Hot Module Replacement - HMR)
HtmlWebpackPlugin
简单创建 HTML 文件,用于服务器访问
I18nWebpackPlugin
为 bundle 增加国际化支持
IgnorePlugin
从 bundle 中排除某些模块

babel 和 webpack 的区别

  1. babel - js 新语法编译工具, 不关心模块化
  2. webpack - 打包构建工具, 是多个 loader plugin 的集合

如何产出一个 lib

  1. 参考 webpack.dll.js
  2. output.library

babel-polyfill 和 babel-runtime 的区别

  1. babel-polyfill 会污染全局
  2. babel-runtime 不会污染全局
  3. 产出第三方 lib 要用 babel-runtime

webpack 如何实现懒加载

  1. import()
  2. 结合 Vue react 异步组件
  3. 结合 VUe-router React-router 异步加载路由

为何 Proxy 不能被 Polyfill

  1. 如 Class 可以用 function 模拟
  2. 如 Promise 可以用 callback 模拟
  3. 但 Proxy 的功能用 Object.defineProperty 无法模拟

webpack 性能优化-构建速度

  1. 优化 babel-loader
  2. IgnorePlugin
  3. noParse
  4. happyPack
  5. ParallelUglifyPlugin
  6. 自动刷新
  7. 热更新
  8. DllPlugin

webpack 优化产出代码

  1. 小图片 base64 编码
  2. bundle 加 hash
  3. 懒加载
  4. 提取公共代码
  5. 使用 CDN 加速
  6. IgnorePlugin
  7. 使用 production
  8. Scope Hosting

gulp 基础知识

Posted on 2020-04-03 | In 前端知识 , 构建工具

参考文档:gulp 官网

快速开始

安装 gulp,作为开发时依赖项

1
npm install --save-dev gulp

在根目录创建 gulpfile.js

1
2
3
4
5
6
function defaultTask(cb) {
// place code for your default task here
cb();
}

exports.default = defaultTask

执行gulp 命令

1
gulp

创建任务(task)

gulp 提供了两个组合方法: series() 和 parallel(),允许将多个独立的任务组合为一个更大的操作。

  1. series() 是按顺序执行任务, parallel() 是并发执行任务.
  2. 这两个方法都可以接受任意数目的任务(task)函数或已经组合的操作.
  3. series() 和 parallel() 可以互相嵌套至任意深度。

举例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
const { series, parallel } = require('gulp');

function clean(cb) {
// body omitted
cb();
}

function css(cb) {
// body omitted
cb();
}

function javascript(cb) {
// body omitted
cb();
}

exports.build = series(clean, parallel(css, javascript));

异步执行

使用callback

如果任务(task)不返回任何内容,则必须使用 callback 来指示任务已完成。在如下示例中,callback 将作为唯一一个名为 cb() 的参数传递给你的任务(task)。

1
2
3
4
5
6
function callbackTask(cb) {
// `cb()` should be called by some async work
cb();
}

exports.default = callbackTask;

你通常会将此 callback 函数传递给另一个 API ,而不是自己调用它。

1
2
3
4
5
6
7
const fs = require('fs');

function passingCallback(cb) {
fs.access('gulpfile.js', cb);
}

exports.default = passingCallback;

使用 async/await

如果不使用前面提供到几种方式,你还可以将任务(task)定义为一个 async 函数,它将利用 promise 对你的任务(task)进行包装。这将允许你使用 await 处理 promise,并使用其他同步代码。

1
2
3
4
5
6
7
8
9
const fs = require('fs');

async function asyncAwaitTask() {
const { version } = fs.readFileSync('package.json');
console.log(version);
await Promise.resolve('some result');
}

exports.default = asyncAwaitTask;

处理文件

gulp 暴露了 src() 和 dest() 方法用于处理计算机上存放的文件。

  1. src() 接受 glob 参数,并从文件系统中读取文件然后生成一个 Node 流(stream)。
  2. 流(stream)所提供的主要的 API 是 .pipe() 方法, 大多数情况下,利用 .pipe() 方法将插件放置在 src() 和 dest() 之间,并转换流(stream)中的文件。
  3. dest() 接受一个输出目录作为参数,并且它还会产生一个 Node 流(stream)。它会将文件内容及文件属性写入到指定的目录中。
1
2
3
4
5
6
7
8
const { src, dest } = require('gulp');
const babel = require('gulp-babel');

exports.default = function() {
return src('src/*.js')
.pipe(babel())
.pipe(dest('output/'));
}

使用插件

Gulp 插件实质上是 Node 转换流(Transform Streams),它封装了通过管道(pipeline)转换文件的常见功能,通常是使用 .pipe() 方法并放在 src() 和 dest() 之间。他们可以更改经过流(stream)的每个文件的文件名、元数据或文件内容。

1
2
3
4
5
6
7
8
9
10
11
12
const { src, dest } = require('gulp');
const uglify = require('gulp-uglify');
const rename = require('gulp-rename');

exports.default = function() {
return src('src/*.js')
// gulp-uglify 插件并不改变文件名
.pipe(uglify())
// 因此使用 gulp-rename 插件修改文件的扩展名
.pipe(rename({ extname: '.min.js' }))
.pipe(dest('output/'));
}

条件插件

因为插件的操作不应该针对特定文件类型,因此你可能需要使用像 gulp-if 之类的插件来完成转换某些文件的操作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const { src, dest } = require('gulp');
const gulpif = require('gulp-if');
const uglify = require('gulp-uglify');

function isJavaScript(file) {
// 判断文件的扩展名是否是 '.js'
return file.extname === '.js';
}

exports.default = function() {
// 在同一个管道(pipeline)上处理 JavaScript 和 CSS 文件
return src(['src/*.js', 'src/*.css'])
// 只对 JavaScript 文件应用 gulp-uglify 插件
.pipe(gulpif(isJavaScript, uglify()))
.pipe(dest('output/'));
}

文件监控

watch() 将 globs 与 任务(task) 进行关联。它对匹配 glob 的文件进行监控,如果有文件被修改了就执行关联的任务(task)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
const { watch, series } = require('gulp');

function clean(cb) {
// body omitted
cb();
}

function javascript(cb) {
// body omitted
cb();
}

function css(cb) {
// body omitted
cb();
}

// 可以只关联一个任务
watch('src/*.css', css);
// 或者关联一个任务组合
watch('src/*.js', series(clean, javascript));

cocos UI 系统

Posted on 2020-04-03 | In 技术

参考文档:cocos 文档

基本元素

  1. Sprite(精灵) 就是图片
  2. Label(文字)

基本布局

Widget(对齐挂件)

操作元素相对目标元素(默认父元素)的定位, 相当于css的绝对定位

Layout(自动布局)

用于元素排列布局
type: 控制元素方向 横排, 竖排, 网格
padding: 和父元素的边距
Spacing: 元素间的间距
direction: 排列方向

scrollView(滚动视图)

目录结构
scrollView

  • scrollBar 滚动条
  • view 上面添加了 mask 组件, 这样只显示元素内的内容
    • content 滚动元素容器
    • item 滚动元素

Prefab(预制资源)

把视图变成资源, 相当于组件

开发流程

Posted on 2020-02-06 | In 开发流程

开发前

任务评估

  1. 任务的分解, 按页面>模块>需求点划分, 如果有时间.
  2. 时间进度的划分, 按半小时划分. 每天工作10个半小时, 即5小时. 为什么是5 小时, 一是方便统计, 二是流出冗余时间.
  3. 时间加权, 根据需求是否明确, 是否有探索性的内容, 是否做过进行加权. eg: 最快时间 + 0.5(没有做过类似模块) + 0.5(需求不明确) + 0.5~1(探索性内容).
  4. 如果有时间, 先写测试用例, 采用TDD 模式开发.

需求讨论

在写任务规划中, 也是对需求的再次理解, 不明确的地方, 用户体验不好的地方都要记下来, 和产品经理讨论.

业务熟悉

如果是在原有代码上的二次开发, 一定要搞清楚原有的业务逻辑. 不然很容易改出bug.

开发中

如何写代码

  1. 开发中要遵循套路(风格), 这样才能反复练习.
  2. 要有工匠精神.
    1. 该写注释的地方写注释.
    2. 该合并变量的时候合并变量.
    3. 注意空行的使用.
    4. 要注意性能.
  3. 要勇于重构.

在开发中有新需求

首先看是否影响原有的开发进度. 如果不影响. 可以改.
如果影响, 区分是否是必须添加的. 如果是必须添加的, 那评估时间并且同步其他人. 如果是非必须的.
区分是否来得及, 如果来的及, 原有需求开发完成后, 放在冗余时间或测试阶段.
如果来不及, 放弃或放在下次迭代.

测试阶段

在测试前首先向qa 要测试用例, 自己测试一遍, 在测试过程中及时跟进.

开发后

  1. 开始几天多点点, 看看有没有bug.
  2. 监测页面的性能.

项目结构化套路

Posted on 2019-03-30 | In 技术

组件的分类

  • 接入型(container) 容器型
  • 展示型
  • 交互型 比如各类加强版的表单组件, 通常强调复用
  • 功能型 比如router-view, transition, 作为一种扩展, 抽象机制存在

开发套路

Posted on 2019-02-21 | In 技术

变量取名

  1. 如果函数要返回一个数据, 函数名字是名词, user
  2. 如果函数只做事不返回, 函数名字是动词, handleClick
  3. 变量名、函数名取名用驼峰法 markedSquare

抽象/封装代码

  1. 在编码复杂需求的时候, 用适当的抽象/封装和良好的代码结构, 可以增加代码的编写效率和可维护性
  2. 一边编写一边抽象, 编写一段时间后停一下, 整理代码结构
  3. 每个方法只做一件事情

代码编写风格

  1. 只有一个入口文件
  2. 要有测试方法
  3. 代码应该描述what,而不是how,因为看代码的人不需要关心一棵树你是怎么去解析的

如何完成复杂程序

  1. 不要慌
  2. 拆分需求,如下所示(以 5-10 分钟能完成为拆分粒度)
  3. 有问题的需求先跳过,最后不做也是行的
  4. 边实现需求边测试,不要到最后一锅粥

组件文件

(风格指南)[https://cn.vuejs.org/v2/style-guide/]

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
31
32
33
34
let page = {
data() {
return {
foo:0,
bar:''
}
},
mounted() {
this._获取数据1()
},
// 总体来说, 方法的顺序按照重要性来排序
methods: {
// 从接口中获取数据的方法
_获取数据1() {

},

// 和页面交互的方法
方法1() {

},
方法2() {

},

// 其它私有方法
_私有方法1() {

},
_私有方法2() {

}
}
}

规划

  1. 套路编写
  2. 发展规划
  3. 基础知识整理
  4. 睡眠

编程能力有哪些方面

  1. 简化问题的能力, 厉害的人不是解决复杂问题, 而是把问题简化
  2. 快速开发的能力, 快速开发需要大量练习
  3. 解决别人不能解决的问题的能力
12…8

Taohong

记录前端技术,学习心得与生活感悟

80 posts
6 categories
65 tags
© 2020 Taohong
Powered by Hexo
|
Theme — NexT.Muse v5.1.4