Imagi_Niha

Imagi_Niha 查看完整档案

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

个人动态

Imagi_Niha 赞了文章 · 2020-04-07

平平无奇,你也可以不看的 前端 Flutter 劝退指南

前言

都 9012 年了,Flutter 有多火,就不需要我多说了,之前掘金首页顶着好长一段时间的 Flutter 视频教程推广足以证明。

那么正如必须要先 ”入门“ 才能 ”出门“,那么前端劝退 Flutter 前也必须得先了解 Flutter。

本文就以前端的角度来给大家捋一捋:在前端眼中,Flutter 的开发到底有何不同?

......然后劝退=_=。【想直接被劝退请滑到最后】

什么是 Flutter

首先,什么是 Flutter?

官网解释:

Flutter是一款 Google 开源的 SDK,可跨平台地为移动端,Web 端,桌面端构建高性能的应用。

当然,当然,虽说是Web端桌面端都能开发,但是我们更多地会着重于 flutter 的移动端跨平台开发功能。

那么,在 flutter之前,其实就有很多跨平台开发的框架了,知名的有 C# 的 Xamarin, 用 js 的有 nativescript ,阿里的 weex 以及大家都比较熟悉的 react native,那么名气不大的就更多了
image

所以,flutter 在一堆跨平台开发框架中凭什么脱颖而出呢?

为什么用 Flutter ?

image

  1. 低投入高产出
    一套代码,直接产出 Android + iOS 两个平台的应用。 这是跨平台开发框架的共同优势,不再多说。
  2. 高效率开发
    通过 Flutter 的 JIT(Just In Time)即时编译功能,能提供 Hot Reload 功能,快速开发应用。有没有 Hot Reload 的开发效率高低,这点前端同学应该是深有体会了。
  3. 丰富优雅的UI
    框架本身提供 Material Design 以及 Cupertino 的两种画风的 UI 组件,不局限于系统本身 OEM 的限制。
  4. 高性能应用
    和原生一样的性能。Flutter 的 AOT 将代码编译成 ARM 二进制,用自身的 自绘引擎(Skia),没有 Bridge 依赖,可直接访问系统底层服务。这些能让 Flutter 性能毫不逊色于 原生应用。

前面的这些都是其次,最关键的是什么呢?

Flutter 有个好“爹”!

大家琢磨一下,当下的主流的三大前端框架,react、react native 是 facebook 的,angular 又是 google 的,只有 vue 是没有大公司背景。事实上社区的很多开源框架,其实都是大企业内部孵化出来的。

有个好爹,背靠 Google 爸爸,含着金钥匙出生,一看就前途不可估量。框架的稳定性和成长性就能得到一定保证,给开源社区信心。

这就是,拼爹一时爽,一直拼一直爽。

言归正传,这里提到 flutter 能不局限于系统 OEM,以及相比其他跨平台框架提供更优秀的性能,那么凭啥就 flutter 那么秀呢?我们可以从 flutter 框架结构上去探索一下。

Flutter 框架结构

flutter 的框架结构图如下:
image

好了,相信大家不只一次看到这一张图了。 懂的可能已经了然于胸,不懂的可能还是一脸懵逼。

这里还是简单说下

从上往下看,
首先是 Framework,Framework 是用 dart 语言写的,从上往下,

  1. 有封装好的 UI 组件 Material 和 Cupertino 【相当于前端的 Ant-Design / Element / iview 等 UI 框架】
  2. 封装好的 UI 组件 Material 和 Cupertino 由更基础的 Widget 组件拼装而成。 【这里的 Widget 相当于 前端的 HTML div, h1, span 等标签元素】
  3. 继续往下, Widget 层是由 Animation(动画), Painting(绘制), Gesture(手势) 共同通过 Rendering 构成的 对象。【这里和前端稍稍有点区别,前端 UI 的结构(html),样式(CSS),事件交互(JS 是分开的,而在 Flutter ,都是 Widget】

Framework 往下是 Engine, Framework 中的 UI 交互都是有 Engine 来进行绘制渲染的。Engine 层内部会通过 Skia 图形引擎画出 UI 组件,Skia 是 Google 开源的 2D 图形引擎,适用于多个平台系统,这也是 flutter 能跨平台的核心元素之一。这也是为什么前面说 flutter 能不局限系统 OEM 组件的限制
也就是说,如果你想要自己封装一个 ant-design 画风的 flutter UI 框架,你可以直接通过基础的 Widget 搭建出自己的 UI 框架。如果底层基础 UI 满足不了你的需求。你可以直接用 dart 调用 Skia 图像引擎的 API,画出自己的 UI,没有任何的限制。

最后是 embedded,嵌入层,这一块是处理平台差异性的事情,从而能够把 flutter 应用嵌入到各个系统平台。

可以看到 Flutter 没有用原生系统上的 OEM,而是用 2D 渲染引擎 skia 直接渲染绘制 UI, 这使得其平台相关层很低,平台只是提供一个画布,剩余的所有渲染相关的逻辑都在Flutter内部,这就使得它具有了很好的跨端一致性。

以上就是 flutter 跨平台开发的结构了, 那么这样设计的优越性在哪呢?我们可以对比下其他应用开发的架构。

跨平台架构对比

Native

首先我们来看下原生APP开发的架构设计,一般一个 App,会分为两大块,分别是 UI 渲染和系统服务调用,我们常说的跨平台开发,其实就是跨的这两块。

image
原生 App 的 UI ,会通过平台提供的原生 OEM 控件实现,

而系统服务调用,如相机,蓝牙等传感器的使用,也会通过平台系统提供的 API 来实现

那么这就会粗线一个问题,不同平台的 OEM 控件和 系统服务调用规范,以及编程语言不统一,Android 使用 Java / Kotlin,而 iOS 使用 Objective-C / Swift,这就产生了平台差异性。

一个 app 要开发几套代码,UI 效果还不一定能保持一致,费时费力。

于是,就产生了跨平台开发的需求。

我们来看下常见的跨平台架构

Webview

image

首先最常见的跨平台方案,是直接用 webview ,这其实就是我们常说的 hybrid app 了。

虽说不同平台的 webview 内核不一定一样,但是总归会遵循 w3c 规范, 那么我们的前端的代码可以运行在平台的 Webview 上,实现 UI 上的跨平台开发

而系统服务调用这一块呢,就通过 bridge 来通过协议来调用原生的方法。

那么 hybrid app 的方案缺点也是很明显的, webview 性能比不上原生

为了解决这个 webview 的性能问题,社区又推出了另外一种方案

React Native / Weex

image
如图所示,React native ,Weex 等框架,是用前端语言描述系统 OEM 之类 实现跨平台,简单的来说,是通过写 js 配置页面布局,然后通过 react native 解析成原生的控件。

这样的做法,就明显提高了性能,因为实质上渲染出来的,还是原生的控件。

但是,即便性能提高了,但是依然达不到原生的层次,因为 RN 是通过 Jscore 解析 jsbunder 文件布局,和原生直接布局还是有那么一丁点差距的。

另外,使用 react native 并不能避免写原生的代码,如果遇到一些平台相关的复杂问题,还是不得不深入到原生库中进行必要的调整。去年 Airbnb 就因为类似的原因放弃了 rn。

那么,用 flutter 就能避免这个问题了么?我们来看下 flutter 的架构

Flutter

image

前面其实也说过了,flutter 的 UI 渲染是基于 skia 图像引擎完成的,不依赖任何一个系统平台,平台仅仅提供一个画布,让 图像渲染在画布上。

那么直接越过原生的渲染机制,从自身的渲染引擎去渲染视图,这就和原生一模一样,没有了中间商赚差价。

两者的渲染性能也提升为了 两者的渲染引擎之间的比较。

至此,我们比较了几种跨平台架构的 UI 渲染实现,

那么关于系统服务的调用呢?Flutter 并没有消除 跨平台 系统服务调用的问题,因为硬件设计层面以及编程语言的差异性是客观存在的,基本无法避免。

但是不同于之前几种用 bridge 的方式来调用系统服务,flutter 用 Platform channel 的形式去调用系统服务,这里先跳过,下面的章节会详细讲一下这里的通信机制

Web VS Flutter

开发语言

image

不同于 Web 把页面分成了 HTML,CSS,JS, 在 Flutter 中,所有东西都是 widgets
具体 widgets 类型分为:

  • 元素 widget。 如 button,menu,list
  • 样式 widget。如 font,color
  • 布局 widget。 如 padding,margin
  • ...

所有的 widget 嵌套组合在一起,就构成了一个 flutter app。

UI 语法

基本样式

关于样式语法,前端的代码我们很熟悉了,用 HTML 和CSS 能快速实现一个简单的 UI。

我们来看看一个最基本的盒子模型:

<div class="greybox">
    Lorem ipsum
</div>
<style>
.greybox {
  background-color: #e0e0e0; /* grey 300 */
  width: 320px;
  height: 240px;
  font: 900 24px Georgia;
}
</style>
var container = Container( // grey box
  child: Text(
    "Lorem ipsum",
    style: TextStyle(
      fontSize: 24.0,
      fontWeight: FontWeight.w900,
      fontFamily: "Georgia",
    ),
  ),
  width: 320.0,
  height: 240.0,
  color: Colors.grey[300],
);

在 flutter ,由于 Flutter 没有标记语言,我们需要嵌套一个个 Widget 类来实现我们的 UI,这里的 Container Widget类,其实就相当于 div 标签。

那么看到这个代码风格,如果有写过 非 jsx 的 react 的话,你会发现代码风格有点像是 React.createElement 的画风。

React.createElement("div", {
    class: "test-c",
    style: "width: 10px;"
}, "Hello", React.createElement("span", null, "world!"));

布局

实现一个 UI ,第二个比较重要的点是布局,
在 Web 前端,实现布局的核心要点是 CSS 的属性:

<div class="greybox">
  <div class="redbox">
    Lorem ipsum
  </div>
</div>
<style>
.greybox {
  background-color: #e0e0e0; /* grey 300 */
  width: 320px;
  height: 240px;
  font: 900 24px Roboto;
  display: flex;
  align-items: center;
  justify-content: center;
}
.redbox {
  background-color: #ef5350; /* red 400 */
  padding: 16px;
  color: #ffffff;
}
</style>

而在 flutter,则需要一些官方提供的样式类来实现,例如这里的 BoxDecoration 类来修饰整个盒子,Alignment 确定文本对齐方式等等

var container = Container( // gray box
  child: Center(
    child:  Container( // red box
      child: Text(
          "Lorem ipsum",
          style: bold24Roboto,
          textAlign: TextAlign.center,
        ),
        decoration: BoxDecoration(
          color: Colors.red[400],
        ),
        padding: EdgeInsets.all(16.0),
      ),
      alignment: Alignment.center,
  ),
  width: 320.0,
  height: 240.0,
  color: Colors.grey[300],
);

交互

Web:

<input name="account" />
<div onclick="handleSubmit()">Submit</div>

最后一点是交互,类似于大部分的前端 UI 框架,每个组件其实都会暴露出一些事件钩子,

通过这些钩子,我们就可以捕获到用户的行为,从而实现对应的逻辑,

这里的 demo 就简单实现了 输入校验, 按钮的点击提交等基本的交互。

Flutter:

// ...
children: <Widget>[
  TextFormField(
    decoration: InputDecoration(
        hintText: 'Email/Name/Telephone',
        labelText: 'Account *',
      ),
      onSaved: (String value) {
        loginForm.account = value;
      },
      validator: (String value) {
        if (value.isEmpty) return 'Name is required.';
      }
  ),
  RaisedButton(
    child: Text(
      'Login'
    ),
    onPressed: () {
      // print('提交操作');
      // dosomething with loginForm
      handleSubmit()
    },
  ),
]

其余的还有 路由,动画,手势等交互,这里不再多说,基本上能用 Web 技术实现的,大都能够在 flutter 实现

可以看到,flutter 的 UI 部分除了没有 jsx 之外,其余部分的设计思想与 react 大同小异。

那么简单介绍完语法,我们来实际操作看看,怎么写一个 APP

开始一个 Flutter App

首先怎么安装 flutter 开发环境这个就不多说了,官网教程教程已经很完善了

目录结构

简单说下目录结构,通过 flutter 创建出一个工程后,会自动生成这样的目录结构,

.
├── android         # Android 平台配置,flutter 自动生成
├── ios             # iOS 平台配置,flutter 自动生成
├── assets          # 静态资源目录
├── build           # 存放构建出的 release 相关文件
├── lib             # 业务代码
├──  └── main.dart  # app 入口
└── pubspec.yaml    # 包管理文件

Hello World

上手一个框架,当然要来一个经典的 Hello World。

要实现一个 flutter app,
我们需要 加载 flutter 的基本组件 import 'package:flutter/widgets.dart';
然后执行基本的 runApp , 那么一个基本的 hello world 就完成了。

// main.dart 文件
import 'package:flutter/widgets.dart';

void main() {
  runApp(
    Center(
      child: Text(
        'Hello, world!'
      ),
    ),
  );
}

效果如下:

可以看到,如果没有样式的话,应用就是一坨黑....

就像在前端开发时我们喜欢使用的 ant design 或者 iview 之类的 UI 框架,开发 flutter 一般也会用 UI 框架

Material App

Flutter 内置两套 UI 组件,分别是 Material UI 和 Cupertino UI,

现在简单看下一个 material 风格的APP是怎么实现的,
首先 import material 组件

new 一个 MaterialApp 组件,配置 title, app bar 等信息,就简单地生成了个 material 画风的 app 了。

import 'package:flutter/material.dart';
void main() {
  runApp(
    MaterialApp(
     title: 'Hello App',
     home: Scaffold(
       appBar: AppBar(
         title: Text('Hello'),
       ),
       body: Center(
         child: Text(
           'Hello Flutter',
           style: TextStyle(
             fontSize: 30
           ),
         )
       ),
     ),
   )
  );
}

更复杂的还可以在这里配置路由相关信息,这里就不再多说。

通过这些我们知道怎么实现一个 flutter app,那么看到所有有实体的元素,都是称为 Widgets, 这里为了方便理解,我们统称为组件。

Widgets

image

而组件又细分为 Stateless Widget 和 Stateful Widget,这里可以很容易联想到 react 的 无状态组件和 有状态组件

事实上 flutter 的这两种组件确实和 react 的差不多

我们首先看下 无状态组件(StatelessWidget)

无状态组件 StatelessWidget

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Hello App',
      home: Scaffold(
        appBar: AppBar(
          title: Text('Hello'),
        ),
        body: Center(
            child: Text(
          'Hello Flutter',
          style: TextStyle(fontSize: 30),
        )),
      ),
    );
  }
}
  • StatelessWidget 对应 react 的函数组件 Functional Component
  • 此处的 build 方法对应 react 的 render 方法

再来看看 状态组件(StatefulWidget)

状态组件 StatefulWidget

Flutter 的状态由两个类组成: 分别是 StatefulWidget 和 State 类。
写法虽然不同,但是概念都大同小异:

  • StatefulWidget 对应 React.Component
  • StatefulWidget 类管理父组件传递的 Prop
  • State 类中管理自身的 State
  • 通过 setState 更新状态
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Counter App',
      home: Scaffold(
        appBar: AppBar(
          title: Text('Counter'),
        ),
        body: Center(
            child: Counter(10) ),
      ),
    );
  }
}


class Counter extends StatefulWidget {
  // 这个类是 state 的配置,可以在此定义父组件传递下来的 prop
  final int increaseNum;
  // 构造函数
  Counter(this.increaseNum);
  
  @override
  _CounterState createState() => _CounterState();
}

class _CounterState extends State<Counter> {
  int _counter = 0;

  void _increment() {
    print('count: $_counter');

    setState(() {
      // setState 的回调告诉 flutter 去变更 当前 State, 并且 setState() 的调用会触发 build() 从而更新视图
      _counter += widget.increaseNum;
    });
  }

  @override
  Widget build(BuildContext context) {
    // 每次调用 setState 都会触发 build 方法,同时,类似于 react 的 render 方法,
    // flutter 框架为了让 重新 build 更加快,也已经对此做了优化
    return Row(
      crossAxisAlignment: CrossAxisAlignment.center,
      mainAxisAlignment: MainAxisAlignment.center,

      children: <Widget>[
        RaisedButton(
          onPressed: _increment,
          child: Text('Increment'),
        ),
        Text('Count: $_counter'),
      ],
    );
  }
}

学过 React 的同学,是不是对此有种似曾相识的感觉呢?

生命周期

组件出来了,生命周期还远么?

类似 React ,Flutter 也有自己的组件生命周期:

  • initState: 初始化状态
  • didChangeDependencies: state依赖关系变更
  • build: 构建视图
  • didUpdateWidget: 状态变更,重新渲染视图
  • deactivate: ui 组件被暂时移除(如切换页面)
  • dispose: ui 被永久销毁

image

到此我们的 UI 组件部分就告一段落。

跨平台开发,“跨” 的除了平台 UI 部分外,还有跨了前面提到的平台系统服务调用

Native 服务调用

不管是哪一个跨平台开发的解决方案,基本上都是在UI层面去完成跨平台,一次开发运行多处,但是当你需要完成特定的功能时,比如:打开相册获取照片,这在这一层面上就无法撼动了,你依然需要使用 Native 的方式来完成。

例如 h5 本身是无法调用系统底层 API 的,在 h5 我们就会用到 jsbridge 来给 native 发送命令,从而让 native 调用系统 API。

而在 flutter, 官方提供了一些插件(plugins packages)来实现常用的功能,例如:本地图片选择,相机功能等,让我们能够简单直接地使用到不同平台的系统接口。

这里也提供了一个唤起相机的 demo :

import 'package:image_picker/image_picker.dart';

class MyHomePage extends StatefulWidget {
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  File _image;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Image Picker Example'),
      ),
      body: Center(
        child: _image == null
            ? Text('No image selected.')
            : Image.file(_image),
      ),
      // 点击按钮进行拍照
      floatingActionButton: FloatingActionButton(
        onPressed: getImage,
        tooltip: 'Pick Image',
        child: Icon(Icons.add_a_photo),
      ),
    );
  }


  Future getImage() async {
    // 打开相机拍摄,并获得图片资源
    var image = await ImagePicker.pickImage(source: ImageSource.camera);

    setState(() {
      _image = image;
    });
  }

}

那么 插件是怎么调用 系统服务的呢?这里就需要用到 flutter 的 methodchannel / platform channel 通信机制。

Native 服务调用-实现自己的 Flutter Plugin

image

如图所示,flutter 通过 methodchannel 机制来调用不同平台 native 层的 api。由于代码最终会被编译成机器码,所以这个调用过程基本上和原生调用差不多,都是无损的,不像通过 bridge 方式调用,需要协议转化。

要自己实现一个 底层服务调用的 FlutterPlugin,可以参考官方文档,简单来说步骤如下:

  1. 定义 plugin package 的 API
  2. 实现不同的底层逻辑

    • Android 平台的功能实现 (Java / Kotlin)
    • iOS 平台的功能实现 (Object-C / Swift)
  3. 在 dart 使用 method channel 调用原生 api

劝退

看了那么多,是不是感觉这不像劝退,而是一篇 Flutter 吹文?

别急,这就劝退了。

Flutter 虽然看起来很强大,但是实际上深入琢磨一下,其实也有不少局限性。

国内环境复杂,小程序横行

大家都知道,国内的流量几乎都被几个大公司垄断, 而 App 的推广下载成本也很高。

所以各大公司才推出了五花八门的小程序,到目前为止,已知的有:

微信/百度/支付宝/字节跳动/ QQ 小程序以及快应用等......

为了快速引流,考虑投入产出比,小公司更愿意会用 小程序/快应用/H5 方案,而不是用获客成本更高的 App 方案。

例如京东的 taro 框架或类似的跨端小程序开发框架,就比 Flutter 更加符合中国特色。

taro 是一个多端统一开发框架,支持用 React 的开发方式编写一次代码,生成能运行在微信/百度/支付宝/字节跳动/ QQ 小程序、快应用、H5、React Native 等的应用。

放弃了 Web 生态

大家可以看到,整篇下来,除了 react-style 的设计思想之外,flutter 和前端其实关系不大

江湖传言道:一切能用js实现的应用,都将用js实现。

但是很可惜的是,基于各方面的考虑,google 选择了 dart 这门并不算热门的语言作为 flutter 的开发语言,而不是 JavaScript / Typescript。给前端开发接触 flutter 设置了一定的门槛。

但是,flutter 也不是只给我们前端用的,站在前端角度,我们当然希望用 js/ts咯。
但这对于 Android/ios 等终端开发来说,其实也是同样需要一定的成本,可以算是一视同仁了。

社区活跃度欠缺

那么又由于 dart 语言这两年才被 flutter 带起来的缘故,之前一直火不起来,直到 flutter 出来后才强行续命。至此 dart 的社区生态,开源库等等都比较欠缺,不像前端社区,有丰富的 npm 包。

那么,大家可以想下,在 flutter 之前,你有听过 dart 语言么?

google 为什么用 dart 作为 flutter 的开发语言呢?

其实是因为……
dart 也有个好爹 Orz,他的爹也是Google。

看到没有,有个好爹多么重要,三线语言 dart 这不就被捧得大红大紫了么[滑稽]

纯前端的局限性

flutter 用自绘引擎彻底解决了 UI 层面的平台差异性,但是前面也提到了,系统硬件服务(如相机蓝牙等服务)的差异性是无可避免的。

作为一个纯前端来说,理想情况下,用 flutter 可以完成所有原生能实现的功能。

但现实往往是不理想的,跨端开发往往会遇到一些平台相关的问题,如 flutter plugin的相机拍照 ,在某个型号的安卓设备上有点小bug。如果你是个纯前端,运气好的话能在开源社区找到解决方案,运气不好,只能向终端(iOS/Android)开发寻求技术支持。

那么,还需要 iOS/Android 开发来兜底的跨端开发框架,还是一个跨端开发框架么?

要开发一个成熟的 App,你敢放心交给纯前端用 flutter 负责么?

当然,这并非是 flutter 弊端,而是所有跨平台方案共同的问题。要是没这问题,react native 早就一统江湖了,airbnb 也不至于弃坑 react native了。

只要跨平台框架还存在需要程序员自行解决的平台差异bug,那么 纯前端程序员全盘负责移动端开发 就是个伪命题。

那么,是不是 flutter 就与前端绝缘了呢?
也并非如此。
如果你要开发一个重 UI 展示 ,调用系统服务比较少的简单应用,那么 flutter 是个不错的选择。

总结

事实上,可以看出,最适合用 flutter 的是哪些程序员呢?

既会 iOS 开发,又懂一些 Android 开发,这不需要太精通, 能搜索解决常见终端问题即可的程序员。那么学 flutter 就是如虎添翼了。

如果真的有前端有志于做一名 flutter 开发工程师,那么不妨简单学习下 Android 和 iOS 开发。

互联网寒冬什么人才最吃香?

多面手,综合性人才,爆栈工程师...

劝退完毕。


本文首发于 github 博客
如文章对你有帮助,你的 star 是对我最大的支持
其他文章:

插播广告:
深圳 Shopee 长期内推
岗位:前端,后端(要转go),产品,UI,测试,安卓,IOS,运维 全都要。
薪酬福利:20K-50K😳,7点下班😏,免费水果😍,免费晚餐😊,15天年假👏,14天带薪病假。 岗位详情参考PS: 网上投递过的就不能内推了哦。
简历发邮箱:chenweiyu6909@gmail.com
或者加我微信:cwy13920
查看原文

赞 4 收藏 1 评论 1

Imagi_Niha 关注了标签 · 2020-04-02

flutter

clipboard.png

Flutter 是 Google 用以帮助开发者在 iOS 和 Android 两个平台开发高质量原生 UI 的移动 SDK。

Flutter is Google’s mobile app SDK for crafting high-quality native interfaces on iOS and Android in record time. Flutter works with existing code, is used by developers and organizations around the world, and is free and open source.

Flutter 官网:https://flutter.dev/
Flutter 中文资源:https://flutter-io.cn/
Flutter Github:https://github.com/flutter/fl...

关注 995

Imagi_Niha 关注了标签 · 2020-04-02

linux

Linux是一种自由和开放源代码的类Unix计算机操作系统。目前存在着许多不同的Linux,但它们全都使用了Linux内核。Linux可安装在各种各样的计算机硬件设备,从手机、平板电脑、路由器和视频游戏控制台,到台式计算机,大型机和超级计算机。

Linux家族家谱图,很全很强大!! 图中可以清楚的看出各个Linux发行版的血缘关系。无水印原图:http://url.cn/5ONhQb

关注 78917

Imagi_Niha 关注了标签 · 2020-04-02

react.js

React (sometimes styled React.js or ReactJS) is an open-source JavaScript library for creating user interfaces that aims to address challenges encountered in developing single-page applications. It is maintained by Facebook, Instagram and a community of individual developers and corporations.

关注 66600

Imagi_Niha 关注了标签 · 2020-04-02

mysql

MySQL是一个小型关系型数据库管理系统,开发者为瑞典MySQL AB公司。在2008年1月16号被Sun公司收购。而2009年,SUN又被Oracle收购。MySQL是一种关联数据库管理系统,关联数据库将数据保存在不同的表中,而不是将所有数据放在一个大仓库内。这样就增加了速度并提高了灵活性。MySQL的SQL“结构化查询语言”。SQL是用于访问数据库的最常用标准化语言。MySQL软件采用了GPL(GNU通用公共许可证)。由于其体积小、速度快、总体拥有成本低,尤其是开放源码这一特点,许多中小型网站为了降低网站总体拥有成本而选择了MySQL作为网站数据库。

关注 72751

Imagi_Niha 收藏了文章 · 2019-05-09

开源的api文档管理系统

在项目中,需要协同开发,所以会写许多API文档给其他同事,以前都是写一个简单的TXT文本或Word文档,口口相传,这种方式比较老土了,所以,需要有个api管理系统专门来管理这些api,从网上找了许多比较好的开源文档管理系统,可以应用到项目中。

1、国外的话Swagger
swagger-ui

2、国内的Showdoc
国内开源的非常好用的一款API文档管理系统,安装也非常方便,只需将源代码放到项目目录下自动安装运行即可,不要要注意PHP版本必须大于5.3.

clipboard.png

3、界面简洁功能强大的EOAPI

4、阿里的RAP
Web API management, free and open sourced, mock data generator, auto test, made by Alibaba, using by 1000+ corporations! Web接口管理工具,开源免费,接口自动化,MOCK数据自动生成,自动化测试,企业级管理。阿里妈妈MUX团队出品!阿里巴巴都在用!1000+公司的选择!一直被抄袭,从未被超越 :3 http://rapapi.net

5、postMan

6、docute: 无需编译的文档撰写工具
像 gitbook 之类的工具需要编译后发布,而 docute 让你直接写 markdown 文件作为文档来显示而不需要编译成 html 这一步,你的文档目录里只需要一个首页 index.html 和你的配置文件 config.js。

docute 会直接渲染这些 markdown 文件为一个单页应用。配合 github pages 发布到 ./docs 目录效果更佳,当然发布到任何地方都可以。

docute 的文档: https://docute.js.org/#/zh-Hans/ (基于 Vue)

Github 项目:https://github.com/egoist/docute

7、SmartWiki 接口文档在线管理系统

clipboard.png

clipboard.png

clipboard.png

clipboard.png

8、SosoApi
SosoApi,编辑Swagger UI的神器

clipboard.png

9、CrapApi开源接口管理系统演示地址

CrapApi:一个由angularjs+bootstrap+springMVC搭建的高性能的免费开源的API接口、文档管理系统(应用接口管理系统)

GitHub源码地址:https://github.com/EhsanTang/...
码云源码地址:http://git.oschina.net/CrapAp...

主要功能:api接口管理、数据字典管理、接口数据模拟、接口文档管理(支持markdown、kindereditor等编辑器)、支持本地部署或在线使用、支持通过建表语句导入数据字典...

其他功能:项目管理、用户管理、模块管理、接口管理、接口版本管理、接口拷贝、接口文档pdf下载、接口mock、模块加密访问、
接口在线调试、数据字典管理、数据字典加密访问多管理员、多权限、多角色管理、自定义菜单、自定义网站样式、文档留言、
错误码管理、接口排序、DOC、PDF、TEXT、EXCEL等资源管理、支持版本号控制、文档内容检索、操作日志记录、
根据日志恢复数据、markdown编辑器、kindeditor编辑器、angularjs编辑器、Lucene搜索、项目成员管理...

站点使用地址:http://api2.crap.cn
效果图:

clipboard.png

clipboard.png

10、 TeaKKi
这个WIKI用来写文档也不错~
TeaKKi == team wiki, https://teakki.com 超越了wiki, 在wiki基础上支持团队知识协作

clipboard.png

11、Web API文档生成工具apidoc

这个需要在编辑后生成静态的HTML页面,然后上传到服务器。

工具名称:apiDoc
Git地址:https://github.com/apidoc/apidoc
项目地址:http://apidocjs.com/
样例项目:http://apidocjs.com/example_b...
apoDoc是从源码的注释中生成RestFul api 文档,样子还是蛮漂亮的……

使用apidoc 生成Restful web Api文档
NodeJS、NPM安装配置步骤(windows版本)

Apidoc安装与使用

具体步骤:

Windows安装方法:
1、官网nodejs.org下载Node

2、安装

3、将npm 替换为淘宝镜像cnpm

C:\Users\Administrator>npm install -g cnpm --registry=https://registry.npm.taobao.org

4、使用cnpm安装apidoc

C:\Users\Administrator>cnpm install apidoc -g

安装的地址:

[apidoc@0.17.5] link C:\Users\Administrator\AppData\Roaming\npm\apidoc@ -> C:\Users\Administrator\AppData\Roaming\npm\node_modules\apidoc\bin\apidoc

C:\Users\Administrator\AppData\Roaming\npm\node_modules\apidoc

apidoc 监视文件变动自动生成工具

监控api_doc.json文件改变,然后自动生成HTML

watch.js文件,然后node 执行该文件

/**
 * watch.js
 * Created by lincoln on 16-1-6.
 */
var gaze = require('gaze');
var exec = require('child_process').exec;
var fs = require('fs')


function init(){
    fs.mkdirSync('./api');
    fs.mkdirSync('./doc');
    createConfigureFile();
    beginWatch();
}
/**
{
  "name": "测试",
  "version": "0.0.1",
  "description": "API文档测试",
  "title": "API文档测试",
  "url" : "http://121.41.44.218",
  "sampleUrl" : "http://121.41.44.218",
  "template":{
    "forceLanguage":"zh-cn"
  }
}
*/

function createConfigureFile(){
    var configure = {
      "name": "测试",
      "version": "0.0.1",
      "description": "API文档测试",
      "title": "API文档测试",
      "url" : "http://xxxxxx",
      "sampleUrl" : "http://xxx",
      "template":{
        "forceLanguage":"zh-cn"
      }
    }
    fs.writeFileSync('./api/apidoc.json',JSON.stringify(configure));
}

function beginWatch(){
    gaze('./api/*.*',function(error,watcher){
     this.on('all', function(event, filepath) {
        console.log(filepath + ' was ' + event);
        runGeneartion();
      })
    });
}

function runGeneartion(){
    var com = exec('apidoc -i ./api -o ./doc ')
    com.stdout.on('data', function (data) {
        console.log("生成Api->"+data);
    });

    com.stderr.on('data', function (data) {
        console.log('生成错误啦->' + data);
    });
}

if(fs.existsSync('./api') && fs.existsSync('./doc')){
    beginWatch();
}else{
    init();
}

安装gaze包,然后执行watch.js文件:

➜  apidoc cnpm install gaze
[gaze@*] installed at node_modules/.1.1.2@gaze (14 packages, use 2s, speed 54.93kB/s, json 85.75kB, tarball 0B)
All packages installed (14 packages installed from npm registry, use 2s, speed 54.62kB/s, json 17(85.75kB), tarball 0B)
➜  apidoc node watch.js
/WEB/apidoc/api/apidoc.json was changed
生成Api->info: Done.

api参数:

Usage: C:\Program Files\nodejs\node.exe apidoc [options]

Options:
   -f, --file-filters      RegEx-Filter to select files that should be parsed (multiple -f can be used)
   -e, --exclude-filters   RegEx-Filter to select files / dirs that should not be parsed (many -e can b
   -i, --input             Input / source dirname.  [./]
   -o, --output            Output dirname.  [./doc/]
   -t, --template          Use template for output files.  [C:\Users\Administrator\AppData\Roaming\npm\
   -c, --config            Path to directory containing config file (apidoc.json)  [./]
   -p, --private           Include private APIs in output.  [false]
   -v, --verbose           Verbose debug output.  [false]
   -h, --help              Show this help information.
   --debug                 Show debug messages.  [false]
   --color                 Turn off log color.  [true]
   --parse                 Parse only the files and return the data, no file creation.  [false]
   --parse-filters         Optional user defined filters. Format name=filename
   --parse-languages       Optional user defined languages. Format name=filename
   --parse-parsers         Optional user defined parsers. Format name=filename
   --parse-workers         Optional user defined workers. Format name=filename
   --silent                Turn all output off.  [false]
   --simulate              Execute but not write any file.  [false]
   --markdown              Turn off default markdown parser or set a file to a custom parser.  [true]
   --line-ending           Turn off autodetect line-ending. Allowed values: LF, CR, CRLF.
   --encoding              Set the encoding of the source code. [utf8].  [utf8]

执行生成文档命令

D:\code\api_doc>apidoc -i example/ -o doc/
info: Done.

D:\POCO\api_doc>

12、PHP_DOC 实时生成 API 文档
https://segmentfault.com/a/11...

clipboard.png

13、Outline
GitHub地址:https://github.com/authing/ou...

截图:

clipboard.png

这个开源的文档界面看起来还挺不错。

14、documize/community
GitHub地址:https://github.com/documize/c...

15、线上文档
①、阿里的 语雀
②、腾讯的 乐享
③、confluence
④、Mindoc

相关文章:
《移动开发接口及文档编写规范》V1.0

查看原文

Imagi_Niha 关注了问题 · 2019-03-14

Vue 加入 withCredentials 后无法进行跨域请求?

Vue和django的前后端分离项目,之前通过在django中允许跨域访问实现了跨域请求,但为了使每个请求带上session信息,我设置了withCredentials ,即:

axios.defaults.withCredentials = true

然后跨域请求时会出现如下问题:

Response to preflight request doesn't pass access control check: The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '*' when the request's credentials mode is 'include'. Origin 'http://localhost:8080' is therefore not allowed access. The credentials mode of requests initiated by the XMLHttpRequest is controlled by the withCredentials attribute.

请问有人知道是什么问题吗?

关注 4 回答 2

Imagi_Niha 赞了回答 · 2019-03-14

Vue 加入 withCredentials 后无法进行跨域请求?

服务端要做以下2个工作:

  1. Access-Control-Allow-Origin 字段必须指定域名,不能为*

  2. Access-Control-Allow-Credentialstrue

关注 4 回答 2

Imagi_Niha 关注了专栏 · 2019-01-24

腾讯云技术社区

最专业的云解读社区

关注 11492

Imagi_Niha 关注了专栏 · 2019-01-24

美团技术团队

“美团技术团队”,你最值得关注的技术团队官方微信公众号。 每周会推送来自一线的实践技术文章,涵盖前端(Web、iOS和Android)、后台、大数据、AI/算法、测试、运维等技术领域。 在这里,与8000多业界一流工程师交流切磋。

关注 12109

认证与成就

  • 获得 9 次点赞
  • 获得 10 枚徽章 获得 1 枚金徽章, 获得 3 枚银徽章, 获得 6 枚铜徽章

擅长技能
编辑

(゚∀゚ )
暂时没有

开源项目 & 著作
编辑

(゚∀゚ )
暂时没有

注册于 2015-08-02
个人主页被 243 人浏览