Skip to content

React

6 posts with the tag “React”

React Native

使用 Gemini 学习新知识效率真的很高

Watchman crawl failed. Retrying once with node crawler

Section titled “Watchman crawl failed. Retrying once with node crawler”

Watchman crawl failed. Retrying once with node crawler. Usually this happens when watchman isn’t running. Create an empty .watchmanconfig file in your project’s root folder or initialize a git or hg repository in your project. Error: watchman —no-pretty get-sockname returned with exit code=1, signal=null,

解决办法:

Terminal window
# Step 1:
watchman watch-del-all
#Step 2:
watchman shutdown-server

1. 修改 gradle.properties 文件(全局配置)

Section titled “1. 修改 gradle.properties 文件(全局配置)”

修改 ~/.gradle/gradle.properties 文件(macOS/Linux)或 C:\Users<YourUserName>.gradle\gradle.properties 文件(Windows) 可以进行全局配置,影响所有使用 Gradle 的项目。

打开或创建上述 gradle.properties 文件。

添加以下配置:

distributionUrl=https\://mirrors.aliyun.com/gradle/gradle-x.x.x-all.zip

2. 使用 init.gradle 文件(更灵活的全局配置)

Section titled “2. 使用 init.gradle 文件(更灵活的全局配置)”

通过 init.gradle 文件可以进行更灵活的全局配置,例如根据不同的条件使用不同的镜像。

  • 在 ~/.gradle/ 目录下创建一个名为 init.gradle 的文件(如果不存在)。

  • 添加以下配置:

allprojects {
buildscript {
repositories {
maven { url 'https://maven.aliyun.com/repository/google/' }
maven { url 'https://maven.aliyun.com/repository/jcenter/' }
maven { url 'https://maven.aliyun.com/repository/central/' }
google()
mavenCentral()
jcenter()
}
}
repositories {
maven { url 'https://maven.aliyun.com/repository/google/' }
maven { url 'https://maven.aliyun.com/repository/jcenter/' }
maven { url 'https://maven.aliyun.com/repository/central/' }
google()
mavenCentral()
jcenter()
}
}

这段配置会将 Maven Central、Google 和 JCenter 仓库都指向阿里云的镜像。

  • 保存文件。

Android NDK (Native Development Kit) 是一套工具,它允许你在 Android 应用中使用 C 和 C++ 等原生代码。 通常情况下,Android 应用主要使用 Java 或 Kotlin 编写,运行在 Android Runtime (ART) 虚拟机上。但有些情况下,使用原生代码可以带来性能上的提升或其他方面的优势。

NDK 的主要用途:

  • 性能密集型任务: 对于需要高性能计算的任务,例如游戏、图形处理、音视频编解码、物理引擎等,使用 C/C++ 等原生代码通常比 Java/Kotlin 更有效率。
  • 代码复用: 如果你已经有使用 C/C++ 编写的现有库或代码,可以使用 NDK 将它们集成到 Android 应用中,避免重复开发。
  • 访问底层硬件: NDK 允许你直接访问一些底层硬件和系统 API,例如传感器、OpenGL ES 等,这在某些特定应用场景下非常有用。
  • 平台移植: 如果你的应用需要在多个平台(包括 Android)上运行,使用 C/C++ 编写核心代码可以更容易地进行移植。

NDK 的组成部分:

  • 编译器和工具链: NDK 包含用于将 C/C++ 代码编译成可在 Android 设备上运行的机器码的编译器(通常是 Clang)和其他构建工具(例如 Make、CMake)。
  • 头文件和库: NDK 提供了一系列头文件和库,允许你访问 Android 平台的各种功能,例如文件 I/O、网络、图形、音频等。
  • 调试器: NDK 提供了用于调试原生代码的工具,例如 GDB。
  • 调试器: NDK提供了用于调试正确代码的工具,例如GDB。
  • JNI (Java Native Interface): JNI 是 Java 和 C/C++ 代码之间进行通信的桥梁。通过 JNI,Java 代码可以调用 C/C++ 函数,C/C++ 代码也可以回调 Java 方法。

构建时报错:

  [1/3] Building CXX object CMakeFiles/appmodules.dir/Users/xiaoxiwang/Work/study-demo/react-native/MyProject/android/app/build/generated/autolinking/src/main/jni/autolinking.cpp.o
  FAILED: CMakeFiles/appmodules.dir/Users/xiaoxiwang/Work/study-demo/react-native/MyProject/android/app/build/generated/autolinking/src/main/jni/autolinking.cpp.o
  ccache /Users/xiaoxiwang/Library/Android/sdk/ndk/25.2.9519653/toolchains/llvm/prebuilt/darwin-x86_64/bin/clang++ --target=aarch64-none-linux-android24 --sysroot=/Users/xiaoxiwang/Library/Android/sdk/ndk/25.2.9519653/toolchains/llvm/prebuilt/darwin-x86_64/sysroot -Dappmodules_EXPORTS -I/Users/xiaoxiwang/Work/study-demo/react-native/MyProject/node_modules/react-native/ReactAndroid/cmake-utils/default-app-setup -I/Users/xiaoxiwang/Work/study-demo/react-native/MyProject/android/app/build/generated/autolinking/src/main/jni -isystem

为什么需要禁用 ccache?

虽然 ccache 通常可以显著提高编译速度,但在某些情况下,它可能会导致问题:

  • 缓存污染: 如果构建环境或依赖项发生重大变化,ccache 的缓存可能变得无效,导致构建错误或不一致的结果。
  • 构建问题排查: 在排查构建问题时,禁用 ccache 可以帮助你确定问题是否与缓存有关。
  • 特定构建配置: 某些特定的构建配置可能与 ccache 不兼容。

通过设置 CCACHE_DISABLE 环境变量是在 React Native 项目中禁用 ccache 的最简单和推荐的方法。

  • 在终端中(临时禁用) : 在运行构建命令之前,在终端中执行以下命令:
Terminal window
export CCACHE_DISABLE=1
npx react-native run-android

或者

Terminal window
CCACHE_DISABLE=1 npx react-native run-android
  • 在 shell 配置文件中(永久禁用): 为了永久禁用 ccache,你可以将 export CCACHE_DISABLE=1 添加到你的 shell 配置文件中, 例如 /.bashrc、/.zshrc 或 ~/.profile。然后,执行 source ~/.bashrc(或相应的命令)来使更改生效。

expo run:androidexpo start --dev-client 命令的区别

Section titled “expo run:android 和 expo start --dev-client 命令的区别”

expo run:androidexpo start --dev-client 是在使用 Expo 开发 React Native 应用时常用的两个命令,它们的功能有所不同,理解它们的区别对于高效开发至关重要。

  • 作用: 启动 Metro Bundler (JavaScript 打包器) 和开发服务器。这个服务器负责提供你的 JavaScript 代码和静态资源给运行在设备或模拟器上的应用程序。
  • 连接方式: 应用程序通过网络连接到这个开发服务器。这意味着你的设备或模拟器需要能够访问运行开发服务器的计算机。
  • 实时更新: 当你修改 JavaScript 代码并保存时,Metro Bundler 会自动重新打包代码,应用程序会自动刷新,实现实时更新 (Fast Refresh)。
  • 开发者菜单: 应用程序可以通过摇晃设备(真机)或使用快捷键(模拟器)打开开发者菜单,其中包含一些调试选项,例如 “Reload”、“Debug JS Remotely” 等。
  • 不构建原生应用: 这个命令本身不构建原生 Android 或 iOS 应用。它只是启动一个开发服务器,供已经构建好的应用连接。
  • 通常与 expo-dev-client 配合使用: 虽然也可以与 Expo Go 配合使用,但为了获得最佳开发体验,尤其是需要使用原生模块或进行更深入调试时,通常与 expo-dev-client 配合使用。
  • 作用: 构建并安装 Android 应用程序到连接的 Android 设备或模拟器。这个命令会: 如果尚未安装 expo-dev-client,则会提示你安装。 根据你的项目配置(包括 JavaScript 代码和原生代码),构建一个包含 expo-dev-client 的 Android 应用程序。 将构建好的应用程序安装到你的设备或模拟器上。
  • 构建原生应用: 这个命令会构建原生 Android 应用(.apk 文件)。
  • 需要设备或模拟器: 这个命令需要在连接的 Android 设备或运行的 Android 模拟器上执行。
  • 首次运行较慢: 首次运行此命令时,由于需要构建应用程序,因此速度较慢。后续运行速度会加快,因为只会重新打包更改的部分。
  • 配合 expo start --dev-client 使用: 构建完成后,通常需要运行 expo start —dev-client 命令来启动开发服务器,以便应用程序可以连接并进行实时更新。

bun add expo-linear-gradient vs bunx expo install expo-linear-gradient

Section titled “bun add expo-linear-gradient vs bunx expo install expo-linear-gradient”

npx expo install expo-linear-gradientbun add expo-linear-gradient 都是在 React Native 项目中安装 expo-linear-gradient 包的命令,但它们使用的包管理器不同,并且针对的项目类型也有所侧重。

  1. npx expo install expo-linear-gradient:
  • 使用的包管理器:Expo CLI (expo)

  • 适用的项目类型:主要用于Expo managed wrokflow 项目

  • 工作方式:

    • npxNode 包执行器,用于执行本地或全局安装的 npm 包。
    • expo installExpo CLI 提供的命令,它不仅会安装指定的包,还会自动处理一些必要的配置,例如更新 package.json 文件、安装兼容的依赖项等。
    • 对于 Expo managed workflow 项目,expo install 会确保安装的包与当前 Expo SDK 版本兼容。这是非常重要的,因为 Expo SDK 版本和依赖包之间存在一定的兼容性要求。
  • 优点:

    • 简单方便,尤其是在 Expo managed workflow 项目中。
    • 自动处理依赖关系和配置,减少手动操作。
    • 确保依赖包与 Expo SDK 版本兼容。
  • 缺点:

    • 只能在安装了 Expo CLI 的环境中使用。
    • 不适用于 bare React Native 项目(除非使用 Expo prebuild)。
  1. bun add expo-linear-gradient:
  • 使用的包管理器: Bun。
  • 适用项目类型: 适用于使用 Bun 作为包管理器的 React Native 项目。包括 Expo managed workflow 项目(在安装了 Bun 的情况下),以及 bare React Native 项目。
  • 工作方式:
    • bun add 是 Bun 提供的命令,用于安装包。
    • Bun 是一个新兴的 JavaScript 运行时和包管理器,旨在提供更快的速度和更好的性能。
  • 优点:
    • 速度通常比 npm 和 yarn 快。
    • 支持多种包管理器格式(npm、yarn、pnpm)。
  • 缺点:
    • 相对较新,生态不如 npm 成熟。
    • 需要单独安装 Bun。

(NOBRIDGE) ERROR Warning: Error: Maximum update depth exceeded.

Section titled “(NOBRIDGE) ERROR Warning: Error: Maximum update depth exceeded.”

在使用Zustnd 时,出现以下错误警告信息:

(NOBRIDGE) ERROR Warning: Error: Maximum update depth exceeded. This can happen when a component repeatedly calls setState inside componentWillUpdate or componentDidUpdate. React limits the number of nested updates to prevent infinite loops.
This error is located at:
in ArtistsScreen
in Unknown (created by Route(index))
in Suspense (created by Route(index))
in Route (created by Route(index))
in Route(index) (created by SceneView)
in StaticContainer
in EnsureSingleNavigator (created by SceneView)
in SceneView (created by NativeStackNavigator)
in RNSScreenContentWrapper (created by ScreenContentWrapper)
in ScreenContentWrapper (created by DebugContainer)
in DebugContainer
in RNSScreen (created by Animated(Anonymous))
in Animated(Anonymous)
in Suspender (created by Freeze)
in Suspense (created by Freeze)
in Freeze (created by DelayedFreeze)
in DelayedFreeze
in InnerScreen (created by Screen)
in Screen
in ScreenStackItem (created by SceneView)
in SceneView (created by NativeStackView)
in RNSScreenStack (created by ScreenStack)
in Unknown (created by ScreenStack)
in ScreenStack (created by NativeStackView)
in RCTView (created by View)
in View (created by SafeAreaProviderCompat)
in SafeAreaProviderCompat (created by NativeStackView)
in NativeStackView (created by NativeStackNavigator)
in PreventRemoveProvider (created by NavigationContent)
in NavigationContent
in Unknown (created by NativeStackNavigator)
in NativeStackNavigator
in Unknown (created by ArtistsScreenLayout)
in RCTView (created by View)
in View (created by ArtistsScreenLayout)
in ArtistsScreenLayout
in Unknown (created by Route(artists))
in Suspense (created by Route(artists))
in Route (created by Route(artists))
in Route(artists) (created by SceneView)
in StaticContainer
in EnsureSingleNavigator (created by SceneView)
in SceneView (created by BottomTabNavigator)
in RCTView (created by View)
in View (created by Screen)
in RCTView (created by View)
in View (created by Animated(View))
in Animated(View) (created by Background)
in Background (created by Screen)
in Screen (created by BottomTabView)
in RNSScreen (created by Animated(Anonymous))
in Animated(Anonymous)
in Suspender (created by Freeze)
in Suspense (created by Freeze)
in Freeze (created by DelayedFreeze)
in DelayedFreeze
in InnerScreen (created by Screen)
in Screen (created by MaybeScreen)
in MaybeScreen (created by BottomTabView)
in RNSScreenNavigationContainer (created by ScreenContainer)
in ScreenContainer (created by MaybeScreenContainer)
in MaybeScreenContainer (created by BottomTabView)
...

原有的实现方式:

export const useArtists = () => {
return useLibraryStore((state) => {
return state.tracks.reduce((acc, track) => {
const existingArtist = acc.find((artist) => artist.name === track.artist);
if (existingArtist) {
existingArtist.tracks.push(track);
} else {
acc.push({
name: track.artist ?? "Unknown",
tracks: [track],
});
}
return acc;
}, [] as Artist[]);
});
};
const artists = useArtists();
const filteredArtists = useMemo(() => {
if (!search) return artists;
return artists.filter(artistNameFilter(search));
}, [artists, search]);

(NOBRIDGE) ERROR Warning: Error: Maximum update depth exceeded. 当组件触发无限循环重新渲染时,通常会发生此错误。

问题在于 useLibraryStore 如何更新 useArtists hook 内的状态。 当前的 useArtists 选择器每次调用时都会创建一个新的Artist对象数组,即使 state.tracks 没有更改也是如此。这是因为reduce总是创建一个新数组。

解决方案:使用 useMemouseArtists 选择器的结果缓存起来,可以避免无限循环。

export const useTracks = () => useLibraryStore((state) => state.tracks);
export const useArtists = () => {
return useTracks().reduce((acc, track) => {
const existingArtist = acc.find((artist) => artist.name === track.artist);
if (existingArtist) {
existingArtist.tracks.push(track);
} else {
acc.push({
name: track.artist ?? "Unknown",
tracks: [track],
});
}
return acc;
}, [] as Artist[]);
};
const artists = useArtists();
const filteredArtists = useMemo(() => {
if (!search) return artists;
return artists.filter(artistNameFilter(search));
}, [artists, search]);

React Native 的 JSI(JavaScript Interface)是 React Native 新架构中的一个关键组成部分,它带来了许多重要的影响,主要体现在性能、灵活性和跨平台一致性等方面。

  1. 性能提升
  • 消除 Bridge 带来的开销:

    • 在旧架构中,JavaScript 和原生代码之间的通信依赖于 Bridge,数据需要在两者之间进行序列化和反序列化,这会带来性能开销。
    • JSI 允许 JavaScript 直接调用原生代码,消除了这种序列化和反序列化的过程,从而提高了通信效率。
  • 同步调用:

    • JSI 使得 JavaScript 可以同步调用原生模块,这对于需要高性能的场景非常有用。
    • 例如,在数据存储、图像处理等场景中,同步调用可以显著提高性能。
  1. 更灵活的集成
  • 直接调用原生模块:
    • JSI 允许 JavaScript 直接持有对 C++ 宿主对象的引用,并对其进行调用,这使得原生模块的集成更加灵活。
    • 开发者可以更方便地使用原生平台的特性和功能。
  • 引擎替换:
    • JSI 使得 JavaScript 引擎(如 JSC)可以更容易地被替换成其他引擎(如 V8)。
    • 这为 React Native 的未来发展提供了更大的灵活性。
  1. 跨平台一致性
  • 共享的 C++ 核心:
    • JSI 为 React Native 提供了一个共享的 C++ 核心,这有助于在不同平台上保持一致的行为和性能。
    • 开发者可以更方便地编写跨平台的原生模块。
  1. 对开发者带来的影响
  • 原生模块开发:
    • JSI 的引入,给原生模块的开发带来了新的工作量。
    • 原生模块的开发者需要使用C++来与javascript进行交互。
  • 性能优化:
    • JSI 为开发者提供了更多优化 React Native 应用性能的手段。
    • 开发者可以利用 JSI 编写高性能的原生模块,从而提高应用的整体性能。

总结

JSI 的引入,是 React Native 架构的一次重大升级,它为 React Native 带来了更高的性能、更大的灵活性和更好的跨平台一致性。

【Antd使用小结】--Table设置行背景色

Table 用于展示行列数据。

指定表格的数据源 dataSource 为一个数据组, 并指定表格列的配置描述columns即可。

const dataSource = [{id:1, name: 'one', color: '#fff'},
{id:2, name: 'two', color: '#eee'},
{id:3, name: 'three', color: '#ddd'}];
const columns = [{
title: 'ID',
dataIndex: 'id',
}, {
title: 'Name',
dataIndex: 'name',
}];
<Table dataSource={dataSource} columns={columns} />

运行结果:

result

默些时候, 需要根据行数据,来指定每行拥有不同的背景色。此时,可以使用Table.rowClassName来指定。rowClassName指定表格行的类名,类型是:Function(record, index):string, 其中record参数就是每行的具体绑定的数据。

<Table
columns={columns}
dataSource={this.state.data}
rowClassName={(record) => record.color.replace('#', '')}
rowKey={record => record.id}/>

rowClassName函数替换每行数据中color的值,将#替换为‘’, 剩下部分作为该行的class名返回。此时,需要制定相应的CSS样式:

.fff {
background: #fff;
}
.eee {
background: #eee;
}
.ddd {
background: #ddd;
}

运行效果:

black

1、https://jsfiddle.net/2b2376a4/

2、https://ant.design/components/table-cn/

【Antd使用小结系列】

Ant Design是蚂蚁金服开源出来的前端UI设计,是一个致力于提升『用户』和『设计者』使用体验的中台设计语言。它模糊了产品经理、交互设计师、视觉设计师、前端工程师、开发工程师等角色边界,将进行 UE 设计和 UI 设计人员统称为『设计者』,利用统一的规范进行设计赋能,全面提高中台产品体验和研发效率。

Ant Design of React是Ant Design 的React实现,开发和服务于企业级后台产品。

  • 提炼自企业级中后台产品的交互语言和视觉风格。
  • 开箱即用的高质量 React 组件。
  • 使用 TypeScript 构建,提供完整的类型定义文件。
  • 基于 npm + webpack + babel 的工作流,支持 ES2015 和 TypeScript。

近来的半年来,带领4个人的小团队(一个安卓、一个Java 实习生、一个Java后台还有我),服务于一个物流项目。一直处于一种全栈开发状态。从需要分析到数据库设计, 从后台服务到一个人扛起前端, 面对多变而不够清晰的需求,紧张的工期等等,虽然很辛苦但充实。问题不断中,不断成长,也发现不一样的自己。

这里作为Antd使用小结系列的开篇, 记录我使用Antd的点滴总结, 强迫自己按时写文章,养成一个好习惯!过去的几个月忙着项目以及生活的事情,都没有心思来总结和沉淀,这算是一个美好的开端吧!

我愿化成一座石桥,经受五百年的风吹,五百年的日晒,五百年的雨打,只求她从桥上走过!

React方法绑定的几种实现方式

翻译参考自:React and ES6 - Part 3, Binding to methods of React class (ES7 included)

在React进行事件方法绑定的时,如下面的代码:

import React , {Component} from 'react'
class user extends Component{
render(){
return <button onClick={this.sayhello} >open hello</button>
}
sayhello(){
console.log('hello world')
console.log(this.props)
}
}
export default user

我们将会得到“TypeError: Cannot read property ‘props’ of null“的错误,如下图所示: error

这是因为当我们调用以这种方式绑定到this的方法时,没有将函数上下文绑定到组件的实例上。这是Javascript的默认行为而且是可以明确预期到的。

React团队在实现支持ES6时不支持自动绑定,你在这篇博客中找到更多关于这样做的原因。

下面将介绍几种不同的方式实现,在JSX中使用ES6语法调用class method。

方法一、 使用Function.prototype.bind()

Section titled “方法一、 使用Function.prototype.bind()”

如下面的代码:

// 使用bind()
class user extends Component{
render(){
return <button onClick={this.sayHello.bind(this)} >open hello</button>
}
sayHello(){
console.log(this.props)
}
}

任何ES6类的方法都是纯JavaScript函数,并从Function prototype继承了bind()方法,所以当我们调用sayHello方法时,this将会指向我们的组件实例。在MDN article可以找到更多的相关信息。

方法二、 使用在constructor中定义的方法

Section titled “方法二、 使用在constructor中定义的方法”
// 使用构造函数定义的方法
class user1 extends Component{
constructor(props){
super(props)
this._sayHello = ()=> this.sayHello();
}
sayHello(){
console.log(this.props)
}
render(){
return (<button onClick={this.sayHello}>open hello 1</button>)
}
}

这种方式中,不再需要在JSX中使用bind(),但会使构造函数更加膨胀。

方法三、使用箭头操函数和构造函数

Section titled “方法三、使用箭头操函数和构造函数”

ES6箭头函数会保留定义时的this上下文,即当调用箭头函数时,函数体内的this对象,就是定义时所在的对象,而不是调用时所在的对象。所以可以利用这个特性,并在构造函数中重新定义sayHello方法。

// 箭头函数 + 构造函数
class user extends Component{
constructor(props){
super(props)
this._sayHello = ()=> this._sayHello()
}
sayHello(){
console.log(this.props)
}
render(){
return <button onClick={this._sayHello.bind(this)} >open hello3</button>
}
}

方法四、使用箭头函数和ES6类属性

Section titled “方法四、使用箭头函数和ES6类属性”

可以通过使用箭头函数和ES6类属性语法:

// 箭头函数 + ES属性语法
class user extends Component{
sayHello = ()=>{
console.log(this.props)
}
render(){
return <button onClick={this.sayHello} >open hello4</button>
}
}

上面出现的代码下在地址: github

使用React写一个简单的Chrome插件

当你手中只有一把锤子的时候,你就会把所有的问题都看成钉子。

Google Chrome,Google公司开发的一款设计简单、高效的Web浏览器。Chrome不仅页面渲染速度快,Javascript执行速度快,而且更重要的是支持开发者为其编写各种各样的扩展来扩充其功能。

这里将要描述的就是为Chrome编写一个插件:使用当前网页地址生成二维码。

给chrome编写插件还是非常容易的事情,这里会使用到react.js(使用react,仅仅是因为,我最近对它很感兴趣)。

使用create-react-app来新建React应用是一个非常不错的选择。

$ create-ract-app react-qr
Creating a new React app in /Users/xiaoxiwang/Documents/demo/react-qr.
Installing packages. This might take a couple minutes.
Installing react, react-dom, and react-scripts...
yarn add v0.24.5
info No lockfile found.
[1/4] 🔍 Resolving packages...
[2/4] 🚚 Fetching packages...
[3/4] 🔗 Linking dependencies...
[4/4] 📃 Building fresh packages...
success Saved lockfile.
success Saved 878 new dependencies.
...

新建完成后生成的文件目录如下:

➜ react-qr tree -L 1
.
├── README.md
├── node_modules
├── package.json
├── public
├── src
└── yarn.lock

此时,运行命令:

$ cd react-qr
$ yarn start

如果顺利将可以在浏览器中,看到一个最简单的Create APP。

清除react-react-app生成的默认样式和内容,为下一步做准备。

Chrome扩展是用于扩充Chrome浏览器功能的程序,是一系列文件的集合,这些文件包括HTML文件、CSS样式文件、JavaScript脚本文件、图片等静态文件以及manifest.json。扩展被安装后,Chrome就会读取扩展中的manifest.json文件。这个文件的文件名固定为manifest.json,内容是按照一定格式描述的扩展相关信息,如扩展名称、版本、更新地址、请求的权限、扩展的UI界面入口等等

在public文件夹下新建一个名为manifest.json的文件, 如下:

{
"manifest_version": 2,
"name": "QR Code",
"description": "Generate QR Code",
"version": "0.1",
"icons": {
"16": "./favicon.png",
"48": "./favicon.png",
"128": "./favicon.png"
},
"browser_action": {
"default_icon": {
"19": "./favicon.png",
"38": "./favicon.png"
},
"default_popup": "index.html",
"default_title": "Generate QRCode"
},
"permissions": [
"activeTab"
]
}

上面的配置中,name定义的扩展名称,version定义扩展版本,description定义扩展的描述,icons则定义的扩展使用的图标及其位置。borowser_action指定了扩展在工具栏中的行为,工具栏中的图标(default_icon)、鼠标悬停插件时的标题(default_title)以及用户点击扩展时显示的页面所在位置(deafult_popup)。

qrcode.react是一个生成二维码的React组件。 添加依赖

$ yarn add qrcode.react

现在编辑App.js, 添加二维码。

import React, { Component } from "react";
import "./App.css";
import QRCode from "qrcode.react";
class App extends Component {
constructor(props) {
super(props);
this.state = { url: "" };
}
renderStatus(url) {
this.setState({ url });
}
render() {
const { url } = this.state;
return (
<div>
<div>QRCode</div>
<div className="url">{url}</div>
<QRCode value={url} size={270} />
</div>
);
}
}
export default App;

二维码的初始值是为空, 大小为270px。文本显示的URL地址,这里使用了一个样式‘url’,定义在APP.css文件中:

.url {
width: 270px;
text-overflow: ellipsis;
white-space: pre;
overflow: hidden;
font-size: 14px;
color: #888;
}

url宽度设置为270px,超出部分自动使用‘…’替代。

现在生成的二维码是一个空字符串,可以在插件页面渲染成功后,获取当前页面的URL地址。这里会是使用到React的componentDidMount方法,该方法会在页面渲染完成时调用一次,此时的DOM结构都已经渲染完成。为了获取到当前页面的URL,可以使用chrome.tabs.query(object queryIfon, function callback)接口。

chrome.tabs.query(object queryInfo, function callback)
Gets all tabs that have the specified properties, or all tabs if no properties are specified.

通过查询当前窗口中激活状态的tab,并选取第一个tab,即获取到了当前页面所在chrome tab。

var queryInfo = {
active: true,
currentWindow: true,
};
chrome.tabs.query(queryInfo, (tabs) => {
const tab = tabs[0];
const url = tab.url;
this.setState({ url });
});

获取到当前页面的url后,使用setState更新状态。

运行yarn buid命令,将打包react app,在build文件夹下生成相应输出文件。

$ yarn build

此时build,会出现chrome is not defined的错误。

➜ react-qr yarn build
yarn build v0.24.5
$ react-scripts build
Creating an optimized production build...
Failed to compile.
./src/App.js
Line 34: 'chrome' is not defined no-undef
Search for the keywords to learn more about each error.
error Command failed with exit code 1.

因为ESLint无法识别chrome, 可以在App.js文件的第一行添加”/global chrome/“,来告知ESLint将chrome识别为一个全局变量。

/*global chrome*/
import React, { Component } from "react";
import "./App.css";
import QRCode from "qrcode.react";

在地址栏中输入”//chrome:extensions”进入chrome插件管理UI, 勾选开发者模式, 并点击“Load upacked extension…”按钮,选择插件的编译输出build目录, 此时就可以在工具栏中,看到一个新增的插件图标,点击这个图标即可生成当前页面地址的二维码。

Demo

React.js 使用小结(开篇)

React.js 使用小结----序

生命不止,奋斗不息

最近三个月,参与了一个企业后台管理系统的小项目,采用前后端分离架构。前端实现技术选型时,选择了React.js。因为之前由大量使用WPF,也接触函数式编程(F#)的经历,所以第一次接触到React.js时就感觉非常的亲切。至少相对于AnglarJS而言,让我感觉更加的舒服。由于这个项目之前,没有项目中实际应用React.js的经验(其他队友也没有这方面的经验),工期也比较紧,选择React.js还是由比较大的风险,所以也促使在具体技术方案的选择上,更加务实了。

并且使用Antd Admin ,一个基于reactant-designdvaMock 企业级后台管理系统最佳实践,基于Mock可以实现脱离后端独立开发,基于dva动态加载Model和路由按需加载,浅度响应式设计。

这些技术可以很好的满足这个项目的需求,直达痛点。前后端同时开发、按需加载、响应式设计、前端UI,这些无疑都是我这个项目急需的特性。

后端开发,选定Spring Boot框架,来简化Spring应用的初始搭建以及开发过程;使用Maven进行包管理,来提供Restful API供前端使用。

由于是同时开发,原先预先定义的开发规则是: 后端的开发人员,先设计API接口,并使用Markdown编写API文档。前端根据API文档,使用Mock数据进行同步开发。通过这种方式来加快开发进度。

理想很丰满,现实真的很骨感。开发过程中,问题不断。由于开发流程上缺乏有力的管理和约束,导致出现较大偏差。

首先、后端开发,没有严格先设计API,而是先进行编码实现后,再来编写API文档。其次、部分队友抵触Markdown写文档,导致没有按照预先的格式编写,导致API文档即使写了,也很难阅读。最终的结果是,配合不顺,开发进度也没有预想的快。前端无法有效的沟通,后端开发人员浪费时间写了不可用和不及时的文档。前后端开发进度不一致,前端只能预设API来Mock数据,在最后联调时,出现大量接口不一致需要返工情形。此外,后端开发人员,开发出一个API接口后,没有做必要的测试工作,导致联调时,一个接口要反复几次,后端才能提供一个无误的接口。Bug的蔓延,导致影响不断扩大。

现在开来,还是对一般程序员先设计再编码的开发方式太乐观了,也许Swagger API 是目前现状的一个不错的解决办法。

此外,前后端,都没有引用测试用例,更别谈测试驱动开发(DDD)。大概上面的人,会认为项目太小没有必要,或是耽误开发进度的缘故。虽然测试驱动开发不是银弹,但也的确能解决一部分问题,重要的还是开发觉悟,即使不完全使用测试驱动开发,测试用例还是大有益处的,这个还是得加强影响。

对这个项目中使用的技术,接下来计划做一个完整的梳理和总结,以每周3篇的速度进行。

  • React组件的三种实现方式
  • Fetch 替代Ajax
  • dva 框架
  • dva-loading
  • redux-saga
  • react-redux
  • React stateless component 使用 this.refs
  • HTML5 上传文件
  • React 一维码(react-barcode, JsBarcode )
  • HTML5 打印
  • Chrome调试JS技巧