有猫饼

有猫饼 查看完整档案

填写现居城市  |  填写毕业院校  |  填写所在公司/组织填写个人主网站
编辑
_ | |__ _ _ __ _ | '_ \| | | |/ _` | | |_) | |_| | (_| | |_.__/ \__,_|\__, | |___/ 个人简介什么都没有

个人动态

有猫饼 发布了文章 · 2020-12-20

webpack 学习笔记(五):plugins 配置

plugins,是 webpack 中另一个很重要的功能。

为什么要使用 plugins

plugins 的作用就是用来拓展 webpack 功能的,它会在整个项目构建过程中生效,执行相关的任务。

plugins 和 loaders 的区别在于,loaders 是在打包构建过程中用来处理源文件的(例如:JSX、sass、less...);而 plugins 插件并不直接操作单个文件,而是直接对整个构建过程起作用。

plugins 可用于执行范围更广的任务,如打包优化、资源管理和重新定义环境中的变量等。

plugins 的基本配置

webpack 的配置项中提供了一个 plugins 属性来进行插件的相关配置,该属性的属性值为数组:

module.exports = {
    // loaders
    plugins: []
}

webpack 中所有需要用到的 plugin,都在该数组中依次进行配置。

HtmlWebpackPlugin

这里我们以一个简单的 HtmlWebpackPlugin 插件为例,来学习 webpack 中 plugin 的使用。

HtmlWebpackPlugin 插件的作用主要有两个:

  1. 创建生成 html 入口文件;
  2. 为 html 文件引入的外部资源,如 CSS、JavaScript;

基本使用

HtmlWebpackPlugin 插件的基本作用就是生成 html 文件,同时可以将 webpack 配置文件中 entry 对应的 js 文件插入到 html 中。

const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
    plugins: [
        new HtmlWebpackPlugin()
    ]
}

实例化该插件时可以不使用任何参数,当然也可以传一个对象作为参数,通过具体的配置项来定制化一些特殊的需求。

HtmlWebpackPlugin 插件提供的配置项比较多,通过源码可以看出具体的配置项如下:

this.options = _.extend({
    template: path.join(__dirname, 'default_index.ejs'),
    filename: 'index.html',
    hash: false,
    inject: true,
    compile: true,
    favicon: false,
    minify: false,
    cache: true,
    showErrors: true,
    chunks: 'all',
    excludeChunks: [],
    title: 'Webpack App',
    xhtml: false
  }, options);

这里我们就不做复杂的讲解了,先学会 webpack 中 plugin 的基本用法,在后续的章节中我们再来深入去学习每一个常用 plugin 的使用。

查看原文

赞 0 收藏 0 评论 0

有猫饼 发布了文章 · 2020-11-23

webpack 学习笔记(四):Loader 配置

Loader,是 webpack 中的第四个核心属性(前面三个分别是 entry、output、mode),我们通常将其翻译成“转换器”。

为什么使用 Loader

因为 webpack 自身只能解析 JavaScript 代码,也就意味着,当我们在项目中使用 less、sass、JSX 等其他浏览器不能解析的代码时,webpack 默认是不能对它们进行处理的。所以这个时候,我们就需要在 webpack 中引入一些额外的工具,让它能够对项目中的非 JavaScript 代码进行转换处理,将其转换成浏览器能够解析的代码。

这些额外的转换工具,就是我们这一章要讲的 Loader 转换器。

Loader 的基本配置

Loader 的配置是在 webpack 配置项的 module 属性里进行的:

module.exports = {
    // loaders
    module: {

    }
}

module 是模块的意思,所以 loader 在这里配置也就表示了它是用来解析与处理模块的。

module 配置项里最重要的一个配置子项就是 rules 属性了,它用来定义不同类型 loader 的不同处理规则:

module.exports = {
    // loaders
    module: {
        rules: [
            {},
            {},
            // ...
        ]
    }
}

rules 是一个数组,数组的每一项都是一个对象。这些对象都用于各类 loader 的转换规则配置。

配置 CSS 资源的打包

这里我们通过对 CSS 样式文件的 loader 配置,来学习 rules 中 loader 的配置语法。

我们在项目中创建一个 src/css/index.css 文件,在里面写上一些简单的样式:

body {
    background-color: #cccccc;
}

h1 {
    color: orange;
}

然后在我们项目的入口 JS 文件 src/index.js 中引入:

import './css/index.css'

image.png

接下来我们先直接执行打包命令 npx webpack 看一下:

image.png

可以看到,打包 ./src/css/index.css 时报错了。这也证明了 webpack 在默认情况下是不能对非 JavaScript 文件进行打包的。

所以接下来,我们就要针对 CSS 对 webpack 进行 loader 的配置。

下载 loader

首先,我们需要在项目根目录执行以下命令来下载专门处理 CSS 的 loader:

npm i style-loader css-loader -D

image.png

配置 loader

下载完成后,开始在对 loader 进行相关配置:

module.exports = {
    // loaders
    module: {
        rules: [
            {
                test: /\.css$/,
                use: ['style-loader', 'css-loader']
            }
        ]
    }
}

image.png

执行打包命令

配置完成后,我们在项目根目录重新执行一次打包命令:

npx webpack

image.png

运行打包后的代码

我们在 index.html 中引入打包后的 JS 文件:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>

<body>
    <h1>hello webpack</h1>
    <script data-original="./dist/bundle.js"></script>
</body>

</html>

打开浏览器:

image.png

可以看到,CSS 样式已经生效。也就意味着,关于 CSS 的 loader 的配置我们就成功了。

loader 配置详解

通过上面一个简单的例子,我们应该对 loader 的使用有了一个比较深刻的印象了。

接下来我们就来具体的讲解一下 loader 配置的一些常用属性。

test

image.png

test 的属性值,是一个正则表达式或正则表达式数组。用于配置模块文件名,当项目中的模块文件名与 test 的正则匹配上时,该模块的代码就会被 use 属性里的 loader 转换处理。

例如我们这个例子中的 /\.css$/,就用于匹配所有以 .css 为结尾的文件。

use

use 的属性值,可以是字符串、对象或数组,都用来表示当前规则要使用的 loader。

如果只配置一个 loader,可以用字符串。例如:

{
    // ...
    use: 'babel-loader'
}

如果 loader 需要额外配置参数,可以用对象,额外参数放在 options 中(有部分 loader 放 query 中)。例如:

{
    // ...
    use: {
        loader: 'babel-loader',
        options: {
            // ... 其他配置
        }
      }
}

如果使用多个 loader 进行链式处理,那么可以取数组,数组的每一项可以是字符串或对象,字符串或对象的使用同上。

{
    test: /\.css$/,
    use: ['style-loader', 'css-loader']
}
{
    test: /\.css$/,
    use: [
        { loader: "style-loader" }, 
        { loader: "css-loader" }
    ]
}

注:数组中的 loader 会按照其在数组中的位置从左到右、从上往下依次执行。

exclude

exclude 的属性值,可以是字符串或正则表达式,字符串需要是绝对路径。当我们有一些文件不想被正则匹配到的 loader 处理时,就可以配置 exclude 排除项。

例如:

{
    test: /\.css$/,
    exclude: /node_modules/,
    use: ["style-loader", "css-loader"]
}

上面的配置文件表示,除了 node_modules 文件夹,对项目中的所有以 .css 为后缀名的文件使用 style-loadercss-loader 处理。

node_modules 中的所有文件都排除在外,可以减少 webpack 解析的范围,从而提高打包效率。

include

include 表示的意思正好与 exclude 相反,它表示只对匹配到的文件进行处理。

{
    test: /\.css$/,
    include: /src/,
    use: ["style-loader", "css-loader"]
}

上面的配置表示,只对 src 目录下所有以 .css 为后缀名的文件使用 style-loadercss-loader 处理。

enforce

对同一类后缀名类型的文件,我们可以使用多个 loader 处理,例如处理 CSS 时使用 ['style-loader', 'css-loader'],loader 的处理顺序是数组项从后向前。如果我们想强制某个 loader 最先处理或最后处理,可以使用 enforce 项。

webpack 推荐使用的 enforce 项有两个:prepost

pre 表示这个 loader 在所有的 loader 之前执行,post 表示这个 loader 在所有的 loader 执行后再执行。

举个例子:

{
    test: /\.js$/,
    use: ['eslint-loader'],
    enforce: 'pre',
    exclude: /node_modules/,
}

这个配置表示在所有处理 JS 文件模块的 loader 执行之前,先执行 eslint-loader,这样我们就可以在 JS 代码未被处理的时候就进行 eslint 代码规范校验。

关于 loader 的一些基本使用我们就先讲这些,后续我们再单独讲一些项目中常用 loader 的配置。

查看原文

赞 0 收藏 0 评论 0

有猫饼 发布了文章 · 2020-11-20

webpack 学习笔记(三):默认配置

webpack 学习笔记:快速入门的练习中,我们除了在项目中安装了 webpack 和 webpack-cli 以外,没有对 webpack 做任何配置,但是 webpack 依然帮我们成功的打包了项目中的 JS 文件。

这是为什么呢?它是怎么知道要打包 src/index.jssrc/a.js 这两个文件的呢?

1. webpack 的默认配置

当我们不对 webpack 做任何配置,直接运行打包命令对项目进行打包操作时,webpack 实际是采用了自身的一些默认配置。

在快速入门的练习中,我们用到了 webpack 的三个默认配置:

  1. 打包入口文件
  2. 打包出口文件
  3. 打包模式

打包入口文件

配置打包入口文件,简单来说就是配置 webpack 要从哪一个文件开始进行打包。

一个前端项目中通常都会有很多的文件,webpack 在构建项目时需要知道到底要从哪一个文件开始进行打包。

webpack 默认的打包入口文件就是 src/index.js,之所以还能将 src/a.js 一同打包,是因为 webpack 在打包前还会分析入口文件中的所有依赖文件,将它们全部加载进来后一同进行打包处理。

我们可以试着将项目中的 src 文件换成其他的名字,例如 public,然后再执行一次打包命令。

image.png

从截图中可以看到,打包过程中报错了。而错误提示就是说“没有在 webpack-demo 目录中找到 src 文件”,因为 webpack 还是在按照默认配置去找 src/index.js 入口文件。

打包出口文件

打包出口文件,简单来说就是 webpack 将项目中的代码打包成功后生成的文件。

webpack 默认的打包出口文件就是 dist/main.js。它会自动在项目根目录创建 dist 目录,然后将打包好的 main.js 文件放入其中。

打包模式

打包模式,指的就是 webpack 在打包时是采用“开发模式”还是“生产模式”进行打包。

webpack 默认的打包模式是“生产模式”。最直观的感受就是打包后的文件,代码全都压缩成了一行。

image.png

当然生产模式的特点肯定还不止这一点,后续我们还会详细的学习。

2. 更改 webpack 的默认配置

虽然啥配置代码都不写就直接打包操作着是很爽,但是在实际项目开发中 webpack 的默认配置可能并不是随时都适用,因此我们还是要学会去更改 webpack 的这些默认配置。

创建配置文件

webpack 的配置文件是项目根目录下的 webpack.config.js。当我们在执行打包命令 npx webpack 时,webpack 会自动寻找该文件并使用其配置信息进行打包。

如果没有该文件,就会使用默认配置进行打包。

因此,我们想要修改 webpack 的默认配置,就需要自己手动去项目根目录中创建一个 webpack.config.js 文件。

image.png

暴露配置对象

所有的构建工具都是基于 Nodejs 平台运行的,因此 webpack 的配置代码,我们也采用的是 CommonJS 的模块化语法(不知道 CommoJS 的可以跳过这句不看,不影响后面的学习)。

webpack.config.js 文件中创建一个对象,再通过 module.exports 暴露出去,后续所有关于 webpack 的配置,我们就都会在这个对象中进行设置。

image.png

配置入口文件

webpack 默认的入口文件是 src/index.js,我们可以通过配置 entry 属性来对入口文件进行修改。

module.exports = {
    entry: './public/index.js'
}

image.png

再重新执行打包命令,就可以成功打包了。

image.png

配置出口文件

webpack 默认的出口文件是 dist/main.js,我们可以通过配置 output 属性来对出口文件进行修改。

const path = require('path');
module.exports = {
    entry: './public/index.js',
    output: {
        path: path.resolve(__dirname, 'build'),
        filename: 'bundle.js'
    }
}

image.png

  • path 属性用于设置打包出口文件所在目录的绝对路径;
  • filename 属性用于设置打包出口文件的文件名;

其中 build 是打包文件所在目录文件夹名,bundle.js 是打包后的文件名。这两个属性值可根据自己项目需求自行修改。

配置完成后,运行打包命令,可以看到打包成功的项目根目录中会生成一个 build/bundle.js 出口文件。

image.png

配置打包模式

webpack 中提供了生产模式 production 和开发模式 development 两种模式来对项目进行打包。

默认的打包模式是生产模式 production,我们可以通过 mode 属性将 webpack 的打包模式更换为开发模式 development

const path = require('path');
module.exports = {
    entry: './src/a.js',
    output: {
        path: path.resolve(__dirname, 'build'),
        filename: 'bundle.js'
    },
    mode: 'development'
}

执行完打包命令后,我们去查看出口文件,可以看到和生产模式打包出来的代码最直接的区别,就是代码没有压缩成一行了,并且还多了一些其他的代码。

image.png

3. 小结

在这一章中,我们学习了 webpack 配置中的三个属性:

  • entry:配置打包入口文件
  • output:配置打包出口文件
  • mode:配置打包模式

但这也只是关于这几个属性最基本的用法,后续我们还会单独针对每一个属性去进行一个更详细的学习。

查看原文

赞 0 收藏 0 评论 0

有猫饼 发布了文章 · 2020-11-20

webpack 学习笔记(二):webpack 简介

通过前面对 webpack 快速入门代码练习的学习,我们对 webpack 应该有了一个初步的印象。这篇文章,我们会更多的站在理论的角度来进一步学习:webpack 是什么?

我们经常会在各种文章中看到关于 webpack 的介绍:

webpack 是一个前端资源构建工具,是一个静态模块打包器。

但是看完之后我们通常还是一脸懵逼,官方的描述对于很多 webpack 的初学者来说太过于专业,导致大家并不能真正的了解到 webpack 到底是干什么的。

所以在这篇文章,我尽量用最简单的例子和描述,来给他们解释清楚,webpack 到底是什么。

前端资源

我们一直说 webpack 是一个前端资源构建工具,这里提到的“前端资源”,指的并不是什么前端资料、文档、博客等,而是在前端项目开发中所需要的 HTML、CSS、JS、图片等资源。前端资源构建,也是针对这些代码资源进行构建。

构建工具

那构建又是什么意思呢?我们代码写好之后,为什么还需要构建呢?
我们来看一下下面这段代码:

header {
    background-color: #cccccc;
    h1 {
        color: #3c3c3c;
    }
}

这是一段用 less 语法写的样式代码,我们在 .html 文件中引入并在浏览器中运行该代码:

image.png

运行后会发现 less 写的样式并没有生效,这是因为浏览器不能解析 less 的语法。

同样的道理,我们在webpack 学习笔记:快速入门一章中练习的代码,也是因为浏览器不能解析 ES6 的模块化语法导致浏览器报错。

image.png

所以,当我们在一个项目中,使用了这些浏览器不能识别的语法去写代码时,我们就需要借助一些工具来帮我们把代码转换成浏览器能够识别的语法。

例如:能将 less 转换为 css 的工具,将 ES6 转换为 ES5 的工具。如果还有其他语法的代码,可能还需要更多的工具。

这个时候,前端就提出了一个“构建工具”的概念,意思就是我们找一个大的工具,将这些小工具的功能都包含进来,开发者只需要学习这个大工具的使用就可以了。

而这个大工具,就是“构建工具”,webpack,就是构建工具的一种。

静态模块打包器

前面我们弄清楚了 webpack 是一个前端资源构建工具,那什么又是静态模块打包器呢?

所谓的静态模块打包器,就是 webpack 会将前端项目中的所有资源文件都当作模块处理,根据模块的依赖关系进行打包编译,最后生成一个能够在浏览器中正常运行的出口文件。

官网的图其实就是一个很好的解释。

image.png

我们再通过一个代码例子来帮助理解,有用过 Vue、React 等前端框架的同学可能会经常看到类似于下面这种的代码(没用过也不影响理解):

// index.js
// 引入 js 资源
import $ from 'jquery';
import './a.js';
// 引入样式资源
import './b.css';
import './c.less';
// 引入图片、字体等资源
// ...

上面的代码中,我们在一个 index.js 文件中通过 import 引入了 .js.css.less 等多个文件。

我们将这个 index.js 文件作为项目的入口文件。webpack 会以入口文件为起点,将该文件中所有依赖的文件引入进来,形成一个代码块,我们称之为 chunk。在 chunk 里面,webpack 会对不同类型的代码分别进行编译处理,将代码都转换为浏览器能够解析的语法,最后将编译完成的代码输出。

这个例子中所有的代码文件,都是“静态模块”,而 webpack 编译这些模块的过程就是“打包”,最后编译完成输出的新文件我们称之为“出口文件”。

最后,我们再回过头来看看文章开头关于 webpack 的描述:webpack 是一个前端资源构建工具,是一个静态模块打包器。

会不会觉得对这句话有了一些自己的理解了呢?

查看原文

赞 1 收藏 0 评论 0

有猫饼 发布了文章 · 2020-11-19

webpack 学习笔记(一):快速入门

所谓快速入门,就是理论这种东西我们都先不讲,直接进入正题,上手去使用 webpack!
但是呢,也只能仅限于入门级的使用,等我们通过基本使用对 webpack 有个初步印象后,我们依然还是需要去从理论从细节去完整的学习 webpack。

1. 项目初始化

在本地新建一个空文件夹来作为这次入门练习的项目根目录。
image-20201116110723347.png

在命令行工具(终端)中进入该文件目录,执行以下命令对项目进行初始化:

npm init -y

image.png

该命令会使用默认参数在项目根目录中创建出 package.json 文件。
image-20201116102526461.png

2. 安装 webpack

在项目根目录中执行以下命令安装 webpack 和 webpack-cli。

npm i --save-dev webpack webpack-cli

image.png

3. 创建项目代码

在项目根目录中新建 src/index.jssrc/a.js 两个文件,代码如下:

// src/a.js
export const name = "有猫饼";
console.log('a.js', name);
// src/index.js
import { name } from './a.js';
console.log(name);

image.png

4. 运行项目代码

在项目根目录创建一个 index.html 文件,将 src/index.js 文件引入。

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>

<body>
    <script data-original="./src/index.js"></script>
</body>

</html>

在浏览器中运行 index.html 代码会发现如下报错:
image.png
报错原因是因为浏览器不能解析在模块以外的文件中使用 import 的语法。

5. webpack 打包 JS 代码

在项目根目录中执行以下命令,打包 JS 文件:

npx webpack

image.png
打包成功后,在项目根目录会自动生成一个 dist/main.js 文件。
image.png
这个 JS 文件,就是对 src/index.jssrc/a.js 打包之后得到的文件。

6. 运行打包文件

index.html 中引入打包之后的 dist/main.js 文件:

<script data-original="./dist/main.js"></script>

刷新浏览器重新运行代码。
image.png
可以看到,打包之后的代码,浏览器就可以正常的解析并运行了。

7. 小结

通过上面这个快速入门的小练习,我们对 webpack 也应该有了一个初步的印象,至少可以小小的总结一些知识点了:

  1. webpack 可以打包 JS 文件;
  2. webpack 可以将浏览器不能识别的 JS 代码打包成浏览器能够识别的 JS 代码;
  3. webpack 可以将多个 JS 文件打包成一个 JS 文件。
查看原文

赞 1 收藏 0 评论 0

有猫饼 发布了文章 · 2020-04-13

Flutter 学习笔记《插件篇》:路由插件 fluro

Flutter 自身是提供了 Navigator widget 来实现应用的路由功能。但使用一些路由插件,可以帮助我们更方便快捷的实现 Flutter 应用页面间的跳转和传值等功能。

这篇笔记我们主要来学习一下 fluro 这个插件。

1. 项目引入 fluro

Flutter 应用中要使用任何第三方插件,都需要先在项目中进行引入。

打开项目根目录中的 pubspec.yaml 文件,在 dependencies 属性下面添加 fluro 的配置代码:

dependencies:
  fluro: ^1.6.3

参考代码截图如下:

image.png

说明:1.6.3 是当前(2020.04.13)fluro 的最新版本号,大家以后在使用时,建议先去官网看看最新的版本号是多少,配置的时候就改成最新版本号即可。

2. fluro 的基本使用

fluro 最基本的功能就是“跳转页面”,其他的“传值”、“返回”等功能对于我们菜鸡来说都是高级操作了,所以先不谈。我们先来学基操!

2.1 建立路由文件

一般情况下,我们都会在项目的 lib 目录中创建一个的新的文件夹(例如:lib/routers),专门用来存放与路由相关的配置代码。

2.2 路由配置

创建一个 lib/routers/routes.dart 文件用来进行路由的相关配置:

import 'package:flutter/material.dart';
// 引入 fluro
import 'package:fluro/fluro.dart';
// 引入路由要跳转的页面文件
import 'package:flutter_fluro_demo/home_page.dart';
import 'package:flutter_fluro_demo/login_page.dart';

class Routes {
  // 各个页面的路径字符串
  static String home = '/home';
  static String login = '/login';
  // 创建一个 configureRoutes 方法,用于路由配置
  static void configureRoutes(Router router) {
    // 配置 home 首页路由
    router.define(home, handler: Handler(
        handlerFunc: (BuildContext context, Map<String, List<String>> params) {
          return HomePage();
        },
      ),
    );
    // 配置 login 登录页路由
    router.define(login, handler: Handler(
        handlerFunc: (BuildContext context, Map<String, List<String>> params) {
          return LoginPage();
        }
      ),
    );
  }
}

在这个配置文件里,我们以“首页”和“登录页”为例,做了一个最基础的路由配置。

2.3 路由静态化处理

为了方便我们后面在各个页面中使用路由,我建议(官方也建议)先对路由做一个静态化处理。

lib/routers 中再创建一个 application.dart 文件,添加以下代码:

import 'package:fluro/fluro.dart';

class Application {
  static Router router;
}

在这段代码中,我们创建了一个 Application 对象,并设置了一个静态属性 router。

在下一步全局注入路由的时候,我们会把调用 Router() 方法得到的路由对象保存在 Application.router 属性中。这样,在后续各个页面中使用路由时,就可以直接通过 Application.router 来拿到路由对象,而不需要反复调用 Router() 方法了。

2.4 全局注入路由

各项配置完成之后,接下来就需要在项目入口文件 main.dart 中注入路由来使我们的路由生效。

import 'package:flutter/material.dart';
import 'package:fluro/fluro.dart';
import 'package:flutter_fluro_demo/routers/routes.dart'; // 路由配置文件
import 'package:flutter_fluro_demo/routers/application.dart';  // 路由静态化文件

void main() {
  final router = Router();  // 获取路由对象
  Routes.configureRoutes(router);  // 调用路由配置方法
  Application.router = router;  // 将路由对象静态化
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      onGenerateRoute: Application.router.generator,  // 将路由配置到 MaterialApp 中
      // ...
    );
  }
}

2.5 路由跳转

fluro 中最简单的跳转方式就是通过 navigateTo() 方法来实现:

import 'package:flutter_fluro_demo/routers/application.dart';
// ...
Application.router.navigateTo(context, '/home');

参考代码截图如下:

image.png
到这一步,我们一个最简单的路由跳转就实现啦。

3. fluro 的进阶使用

路由的基本使用学会了之后,接下来我们就需要增加一丢丢的难度了。

3.1 拆分路由配置文件

在前面的路由基础用法里,我们将所有关于路由的配置都放在了 routes.dart 文件中,当我们项目的页面逐渐增多时,就会导致这个文件变得越来越大,代码越来越多,后期维护起来也会越来越不方便。

所以,我们要对 routes.dart 文件中的代码进行拆分。

routes.dart 原本的代码如下:

image.png

从图中代码可以看到,路由操作的大部分代码都集中在了 handler 属性中,因此,我们就把每一个 handler 属性的属性值都提取出去。

lib/routers 中再创建一个文件:route_handlers.dart,将拆分出来的代码放进去:

import 'package:flutter/material.dart';
import 'package:fluro/fluro.dart';

import 'package:flutter_fluro_demo/home_page.dart';
import 'package:flutter_fluro_demo/login_page.dart';

Handler homeHandler = Handler(
  handlerFunc: (BuildContext context, Map<String, List<String>> params) {
    return HomePage();
  },
);

Handler loginHandler = Handler(
  handlerFunc: (BuildContext context, Map<String, List<String>> params) {
    return LoginPage();
  },
);

这样拆分后,我们原本的 routes.dart 文件就变得很干净了。

import 'package:fluro/fluro.dart';
import './route_handlers.dart';

class Routes {
  static String home = '/home';
  static String login = '/login';

  static void configureRoutes(Router router) {
    router.define(home, handler: homeHandler);
    router.define(login, handler: loginHandler);
  }
}

3.2 路由传参

虽然我们把路由传参划分到了进阶用法里,但是实际上我们应该都清楚,任何一个 App 里,路由传参功能都是必不可少的。更何况,fluro 的路由传参其实 so easy~

路由传参其实简单理解为一个点外卖的过程。例如我们要将 home 页面的数据传递 detail 页面,那么 home 页面就是商家,detial 页面就是整天宅在家里扣 jio 的你,而 fluro,就是我们的外卖骑手。

理解了传参的流程之后,接下来我们就可以开始搬砖了。

第一步:在 detial 页面配置要接收的参数

detail 页面中的代码如下:

import 'package:flutter/material.dart';

class DetailPage extends StatelessWidget {
  DetailPage({this.detailId});
  final String detailId;

  //...
}

这里我们定义了一个 detailId,用来接收后面 home 传递过来的参数。

我的案例代码截图如下:

image.png

第二步:在 home 页面设置要传递的参数

home 页面的路由跳转和传参代码如下:

RaisedButton(
  child: Text('去详情页'),
  onPressed: () {
    Application.router.navigateTo(context, '/detail/001');
  },
),

/detail/001 中的 /detail 就是我们要去到的 detail 页面的路径,001 就是我们要传递给 detail 页面的参数。

第三步:在路由配置文件中对 detail 路由进行配置

打开 lib/routers/routes.dart 文件,对 detail 的路由进行如下配置:

class Routes {
  // ...
  static String detail = '/detail/:id';

  static void configureRoutes(Router router) {
    // ...
    router.define(detail, handler: detailHandler);
  }
}

参考代码截图如下:

image.png

detailHandler 报错是因为我们还没创建这个变量,下一步创建好就不会报错了。)

/detail/:id 中的 id 是我们自己定义的一个参数名,是对 home 页面传递的参数进行命名。

接下来,再打开 lib/routers/routes_handlers.dart 文件,配置 detail 的 handler 方法。

import 'package:flutter_fluro_demo/detail_page.dart';
// ...
Handler detailHandler = Handler(
  handlerFunc: (BuildContext context, Map<String, List<String>> params) {
    String id = params['id'].first; // 通过 params 获取到路由中传递的参数
    return DetailPage(detailId: id);
  },
);

到此,我们 home 页面,通过路由往 detail 页面的传参就已经完成了。

image.png

3.3 返回页面

在 Flutter 中,跳转后的页面左上角会自带一个返回箭头,但是有的时候,我们也需要点击界面其他地方来实现页面的返回,又或者,我们想返回到一个指定的界面。

3.3.1 返回到上一页面

页面返回的代码很简单:

RaisedButton(
  child: Text('返回上一页'),
  onPressed: () {
    // flutter 
    Navigator.pop(context);
    // fluro
    // Application.router.pop(context);
  },
)

从上面代码可以看出,实现“返回上一页面”功能的方式两种:一种是 flutter 自带的 Navigator widget 提供的 pop 方法;另一种是 fluro 中提供的 pop 方法。

这里我们列出 flutter 原生的返回方式,是因为 flutter 原生的方式更简洁一些,所以路由的返回功能我们更多会选择直接用 Navigator 的方法返回。

3.3.2 返回到指定页面

fluro 中并没有提供专门的“返回到指定页面”的方法(我有看 fluro 的源码,确实没有找到,如果这里说法有误请大家评论里告诉我)。所以如果我们想要实现“返回到指定页面”的效果,可以直接用 navigateTo 页面跳转方法实现。

具体代码可以参考前面「2.5 路由跳转」的示例代码。

3.4 管理历史记录

默认情况下,每一次的路由跳转,都会有一个新的路由记录注册到路由表中。

fluro 中的 navigateTo() 方法可以设置第三个参数来管理路由历史记录。

3.4.1 替换历史记录

替换历史记录是指用“跳转后的新页面”记录替换掉“当前页面”记录。将 navigateTo() 方法的第三个参数设置为 replace: true 即可。

child: RaisedButton(
  child: Text('去详情页'),
  onPressed: () {
    Application.router.navigateTo(context, '/detail/001', replace: true);
  },
),

参考代码截图如下:

image.png

当我们从“登录页”跳转到“首页”,再从“首页”跳转到“详情页”,然后点击“详情页”左上角的返回按钮,可以发现,直接返回到了“登录页”。因为我们的历史记录里面已经没有保留“首页”的记录了。

3.4.2 清空历史记录

除了替换一条历史记录外,navigateTo() 方法还可以将第三个参数设置为 clearStack: true 来实现清空历史记录的效果。

child: RaisedButton(
  child: Text('去详情页'),
  onPressed: () {
    Application.router.navigateTo(context, '/detail/001', clearStack: true);
  },
),

4. 最后

fluro 的用法大概就是这些了,如果还有没写到的知识点,大家可以在评论里告诉我,我再补充。

查看原文

赞 5 收藏 4 评论 3

有猫饼 发布了文章 · 2020-04-07

Flutter 学习笔记《样式篇》:颜色 Color

“Color(颜色)”是我们在设置应用程序界面样式时最常用到的属性。

Flutter 中颜色设置的方式有很多种,其中最常用的有下列几种:

Color c1 = Color(0xFF0099ff);
Color c2 = Color.fromRGBO(60, 170, 250, 1);
Color c3 = Color.fromARGB(255, 60, 170, 250);
Color c4 = Colors.blue;
Color c5 = Colors.red[300];

这里面涉及到了两个类:Color 和 Colors。

1. Color

Color 是 Flutter 提供的一个颜色类。

1.1 Color()

调用 Color 构造函数时,传入一个颜色的 ARGB 值,即可设置对应的颜色。示例代码如下:

Color c1 = Color(0xFF0099ff);
  • FF 是透明度的十六进制表示方式,取值范围是:00 ~ FF(透明 ~ 不透明);
  • 0099ff 是颜色 RGB(red/green/blue) 值的十六进制表示方式,不同的颜色对应的值也不一样,建议通过取色器软件等进行取色。

1.2 Color.fromRGBO()

Color 类的身上有一个 fromRGBO() 的方法,该方法接受 4 个参数,分别与 RGBO 对应:red、green、blue、opacity。示例代码如下:

Color c2 = Color.fromRGBO(60, 170, 250, 1);
  • RGB 表示红绿蓝三种颜色,取值范围是:0 ~ 255;
  • O 表示颜色透明度,取值范围是:0 ~ 1(透明 ~ 不透明);

1.3 Color.fromARGB()

Color 类的身上还有一个 fromARGB() 的方法,该方法同样接收 4 个参数,与 ARGB 对应的也是:alpha、red、green、blue。示例代码如下:

Color c3 = Color.fromARGB(255, 60, 170, 250);
  • A 表示颜色透明度,取值范围是:0 ~ 255;
  • RGB 表示红绿蓝三种颜色,取值范围是:0 ~ 255(透明 ~ 不透明);

2. Colors

Colors 实际上就是 Material 库中对 Color 的进一步封装。它将一些常用颜色的十六进制值封装成了我们更熟悉的英文单词形式。示例代码如下:

Color c4 = Colors.red;
Color c5 = Colors.white;

Color c6 = Colors.red[50];
Color c6 = Colors.blue[900];

颜色后面的中括号是用来设置当前颜色的深浅度,可取的值有 10 种:50、100、200 ... 900,值越大,对应的颜色越深。500 就等同于当前颜色自身。

注:并不是所有的颜色英文单词都能用,只能使用 Material 中封装了的颜色。
查看原文

赞 2 收藏 0 评论 0

有猫饼 发布了文章 · 2020-01-10

快速启动 Xcode 中的模拟器

由于每次启动 Simulator 模拟器都要先从 Xcode 中打开一个项目,感觉太麻烦了,所以在网上找了一个解决办法,能够在不打开 Xcode 的情况下独立启动 Simulator 模拟器。

1. 显示 Xcode 包内容

如下图所示,找到应用程序中的 Xcode,【右键】选择【显示包内容】。
image.png

2. 复制 Simulator

如下图所示,找到 Applications 目录下的 Simulator 模拟器,并复制。
image.png

3. 拷贝 Simulator

如下图所示,将复制好的 Simulator 模拟器拷贝到应用程序中。
image.png

4. 完成

以后每次再需要启动 Simulator 模拟器时就可以直接点击应用程序中的 Simulator 打开了。

查看原文

赞 2 收藏 1 评论 0

有猫饼 赞了回答 · 2019-12-17

解决loading 动画 放在请求拦截里做统一处理,还是放在每个页面里

其实就是逻辑统一处理,还是每个页面单独处理的区别。

简单聊聊我所知道的几种不同方案

image.png

一、在请求接口时拦截,例如axios的拦截器

这个方案就是题主说的第一种方案。这种方案如果结合vue或者react中使用,那么我们必须要思考的一个问题是,这个loading是代表的那种loading。

很显然,一定得是全局loading,否则很多细节无法处理,例如如果有多个接口同时请求怎么办?因此,要把这种方案实施下来,就需要

  1. Loading组件独立于所有页面组件,是公用的全局组件
  2. 多个请求时,需要计算正在请求的个数

如果从代码量考虑,这样的方式确实减少了工作量,但是缺陷也很明显,缺乏灵活性。只要有接口请求,就只能把这个全局Loading抬出来。很多场景无法适应。

当然,如果是后台管理系统,不需要Loading具有太多的灵活性,是可以这样处理的。就看你能不能说服团队其他所有人,这个项目,所有的加载情况都这样处理。

二、在组件中拦截,例如ant design pro中使用的dva-loading

dva-loading是react-redux解决方案中的中间件。dva-loading在全局store中维护了一个EffectLoading的对象,所有loading的状态请求都放在了该对象中,并且每个请求根据namespace的不同都有对应的唯一值。也就是说,这种方式避免了整个全局中有一个loading,而是把所有的loading状态各自有一个唯一的标识能够访问得到。

// EffectLoading
{
  effects: {
    'login/fetch': true,
    'login/user': true,
    'dashboard/fetch': false,
    'dashboard/list': false,
  },
  global: false,
  models: {
    'login/fetch': true,
    'login/user': true,
  }
}

这种方式也是统一处理,但是处理的方式和第一种方式不一样,这种方式更具灵活性,我们可以在组件里针对自己当前组件的状态进行不同的处理。该方式仅仅只是把对应请求的loading传入组件,在组件中如何处理,则自己决定。

这种方式由于第一种方式,当然两种方式的处理思维也可以结合起来,糅合成更合理的方案

但是这样的方案在react中也并非没有缺陷。

dva-loading让整个项目全局共享一个loadingEffect对象。

也就是说,无论你的组件如何划分,loading都不可避免的成为了共享状态。可是在很多情况下,我们仅仅只希望loading成为当前页面或者某个子组件的私有状态。因此dva-loading虽然看上去简化了写法,却让组件化思维固化,失去了灵活性。

也正因为处于最顶层的共享数据,当loadingEffect状态改变时,会导致额外的冗余渲染。因此,dva-loading应该慎用。

三、每个组件,每个请求维护自己的loading状态

当然,这样的方式灵活性都很好,无论交互想要怎么处理,都可以完美解决。缺点也很明显,完美要额外定义很多loading状态,维护成本会稍高,代码也会多些一点。

四、在react中,利用自定义hooks

这是目前我认知到的最完美的方案。

超性感的的react hooks(五):自定义hooks中我跟大家介绍了如何使用自定义hooks。
并在使用hooks重构antd pro的想象力(三)我是如何利用hooks干掉redux的中介绍了如何自定义一个能够处理请求的hooks。

首先我们定义一个全局的能够处理请求的自定义hooks useInitial

【详细代码见上述文章】

在组件中使用时,只需要如下一句代码即可拿到对应请求的loading状态和请求数据

const {loading, data} = useInitial(api);

该方案是将请求逻辑统一封装成为自定义hooks,极大降低了维护成本,并且每个loading都相互独立,互不影响,灵活性也不存在任何问题。

因此我认为这是最完美的解决方案。不过要基于react hooks。

五、选择最适合自己的方案

结合自己团队和项目的情况,选择最适合自己的方案,才是最好的方案!

关注 12 回答 10

有猫饼 赞了回答 · 2019-12-17

解决关于line-height居中的兼容性问题

移动端网页开发中,经常会涉及到一些小按钮或者小标签,比如这种:

对于一般 PC 浏览器以及 iOS 设备的浏览器表现就是我们想要的居中效果,但是大部分 Android 设备的浏览器文字都会稍微向上偏离,如下图所示:

测试表明,字体字号为奇数的两倍时(比如 10px、14px、18px、22px、26px),会出现严重偏移现象。

其实系统之间效果的差异跟我们的字体类型、系统排版引擎、浏览器都有关系,其实不影响用户浏览与操作等体验,我们可以忽略这些问题,对于一些居于使用场景偏离的比较明显的,可以使用下面提到的两种处理方案。

方案一:

我们可以通过 transform: scale 来处理,比如,字体大小是 8px,我们把字体设定为 16px,然后通过 scale(0.5) 缩放至一倍大小,简单粗暴。

注意:放大两倍会使得容器被撑开占位。

方案二:

结合行高、对齐的关系,结合伪元素得出的黑科技,亲测效果很理想。

.jd::before {
    content: '';
    display: inline-block;
    vertical-align: middle;
    width: 0;
    height: 100%;
    margin-top: 1px;
}

答案来源:

https://github.com/o2team/H5Skills/issues/4

关注 4 回答 2

认证与成就

  • 获得 24 次点赞
  • 获得 2 枚徽章 获得 0 枚金徽章, 获得 0 枚银徽章, 获得 2 枚铜徽章

擅长技能
编辑

(゚∀゚ )
暂时没有

开源项目 & 著作
编辑

(゚∀゚ )
暂时没有

注册于 2018-07-09
个人主页被 660 人浏览