詳細剖析|袋鼠雲數棧前端框架Antd 3.x 升級 4.x 的踩坑之路

来源:https://www.cnblogs.com/DTinsight/archive/2023/03/03/17176483.html
-Advertisement-
Play Games

袋鼠雲數棧從2016年發佈第⼀個版本開始,就始終堅持著以技術為核⼼、安全為底線、提效為⽬標、中台為戰略的思想,堅定不移地⾛國產化信創路線,不斷推進產品功能迭代、技術創新、服務細化和性能升級。 在數棧過去的產品迭代中受限於當前組件的版本,積累了很多待解決的問題,隨著新的功能需求不斷增加,很多原先的組件 ...


袋鼠雲數棧從2016年發佈第⼀個版本開始,就始終堅持著以技術為核⼼、安全為底線、提效為⽬標、中台為戰略的思想,堅定不移地⾛國產化信創路線,不斷推進產品功能迭代、技術創新、服務細化和性能升級。

在數棧過去的產品迭代中受限於當前組件的版本,積累了很多待解決的問題,隨著新的功能需求不斷增加,很多原先的組件以及交互設計需要進行優化。

2月,伴隨著數棧 UI5.0 的煥新升級,數棧前端團隊一起將組件框架 antd 從 v3.x 升級到了 v4.x,更新組件的 UI,提升產品的交互體驗,使數棧產品能夠更加靈活地適應未來產品功能迭代的需求。

本文將總結歸納袋鼠雲數棧前端框架Antd 從3.x 升級到4.x 的相關步驟,及在這個過程中踩過的坑,解決的問題。

相容性問題

第三方依賴相容問題

· React - 最低 v16.9,部分組件使用 hooks 重構 ( react 升級相關文檔:https://sourl.cn/6bM4Ep)

· Less - 最低 v3.1.0,建議升級到 less 4.x;

· @ant-design/icons-antd - 不再內置 Icon 組件,請使用獨立的包。

對 3.x 的相容性處理

或許是考慮到部分組件升級的毀壞性,antd4.x 中依然保留了對 3.x 版本的相容,廢棄的組件通過 @ant-design/compatible 保持相容,例如 Icon、Form。

註意:建議 @ant-design/compatible 僅在升級過程中稍作依賴,升級 4.x 請完全剔除對該過渡包的依賴。

升級步驟

只有一步

@ant-design/codemod-v4 自帶升級腳本,會自動替換代碼。

# 通過 npx 直接運行
npx -p @ant-design/codemod-v4 antd4-codemod apps/xxxx

# 或者全局安裝
# 使用 npm
npm i -g @ant-design/codemod-v4
# 或者使用 yarn
yarn global add @ant-design/codemod-v4

# 運行
antd4-codemod src

註意:該命令和腳本只會進行代碼替換,不會進行 AntD 的版本升級,需要手動將其升級至4.22.5。

該命令完成的工作:
    1. 將 Form 與 Mention 組件通過 @ant-design/compatible 包引入 
    2. 用新的 @ant-design/icons 替換字元串類型的 icon 屬性值 
    3. 將 Icon 組件 + type =“” 通過 @ant-design/icons 引入 
    4. 將 v3 LocaleProvider 組件轉換成 v4 ConfigProvider 組件 
    5. 將 Modal.method() 中字元串 icon 屬性的調用轉換成從 @ant-design/icons 中引入

antd4-codemod

file

上圖這類報錯是 Icon 組件自動替換錯誤,有 2 種處理方式:

· 報錯文件的 Icon 比較少的情況,可以直接手動替換該文件中的 Icon 組件,具體替換成 Icon 中的哪個組件可以根據 type 在 Icon 文檔中找( Icon文檔:https://sourl.cn/neHBVS );

· 下圖中是具體報錯的節點,可以看到 JSXSpreadAttribute 節點也就是拓展運算符中沒有 name 屬性,所以把 Icon 組件的拓展運算符改一下再執行替換腳本就可以了。

file

antd4 問題修複

styled-components

styled-components 依賴需要轉換寫法。

file

Icon

不要使用相容包的 icon。
在 3.x 版本中,Icon 會全量引入所有 svg 圖標文件,增加了打包產物;
在 4.x 版本中,對 Icon 進行了按需載入,將每個 svg 封裝成一個組件。

註意:antd 不再內置 Icon 組件,請使用獨立的包 @ant-design/icons。

· 使用

import { Icon } from 'antd';
mport { SmileOutlined } from '@ant-design/icons';

const Demo = () => (
  <div>
     <Icon type="smile" />
     <SmileOutlined />
     <Button icon={<SmileOutlined />} />
  </div>
);

· 相容

import { Icon } from '@ant-design/compatible';
const Demo = () => (
  <div>
    <Icon type="smile" />
    <Button icon="smile" />
  </div>
);

Form

antd Form 從 v3 到 v4:https://sourl.cn/7TiRfp

● Form.create()

在 3.x 中,表單中任意一項的修改,都會導致 Form.create() 包裹的表單重新渲染,造成性能消耗;
在 4.x 中,Form.create() 不再使用。
如果需要使用 form 的 api,例如 setFieldsValue 等,需要通過 Form.useForm() 創建 Form 實體進行操作。

· 函數組件寫法

// antd v4
const Demo = () => {
  const [form] = Form.useForm();

  React.useEffect(() => {
    form.setFieldsValue({
      username: 'Bamboo',
    });
  }, []);

  return (
    <Form form={form} {...props}> ... </Form>
  )
};

· 如果是 class component,也可以通過 ref 獲取

class Demo extends React.Component {
  formRef = React.createRef();

  componentDidMount() {
    this.formRef.current.setFieldsValue({
      username: 'Bamboo',
    });
  }

  render() {
    return (
      <Form ref={this.formRef}>
        <Form.Item name="username" rules={[{ required: true }]}>
          <Input />
        </Form.Item>
      </Form>
    );
  }
}

當我們使用 From.create() 的時候,可能會傳入參數,做數據處理,例如:

export const FilterForm: any = Form.create<Props>({
  onValuesChange: (props, changedValues, allValues) => {
    const { onChange } = props;
    onChange(allValues);
  },
})(Filter);

由於 Form.create 的刪除,需要放到 < Form> 中。

<Form
  ref={this.formRef}
  layout="vertical"
  className="meta_form"
  onValuesChange={(_, allValues) => {
    const { onChange } = this.props;
    onChange(allValues);
  }}
>

● getFieldDecorator

在 4.x 中,不在需要 getFieldDecorator 對 Item 進行包裹。註意以下問題:

· 將之前寫在 getFieldDecorator 中的 name/rules 等移到屬性中;

· 初始化在 form 中處理,避免同名欄位衝突問題;

· 關於表單聯動的問題,官方提供了 shouldUpdate 方法。

const Demo = () => (
  <Form initialValues={{ username: 'yuwan' }}>
    <Form.Item name="username" rules={[{ required: true }]}>
      <Input />
    </Form.Item>
  </Form>
);

● initialValue

· 歷史問題

initialValue 從字面意來看,就是初始值 defaultValue,但是可能會有部分同學使用他的時候會誤以為 initialValue 等同於 value。造成這樣的誤解是因為在 3.x 的版本中,一直存在一個很神奇的問題,受控組件的值會跟隨 initialValue 改變。

看下麵的例子,點擊 button 修改 username, input 框的 value 也會隨之改變。

const Demo = ({ form: { getFieldDecorator } }) => (
  const [username, setUsername] = useState('');
  const handleValueChange = () => {
    setUsername('yuwan');
  }
  return (
    <Fragment>
      <Form>
        <Form.Item>
          {getFieldDecorator('username', {
             initialValue: username,
             rules: [{ required: true }],
          })(<Input />)}
        </Form.Item>
      </Form>
      <Button onClick={handleValueChange}>Change</Button>
    </Fragment>
  )
);

const WrappedDemo = Form.create()(Demo);

但當 input 框被編輯過,initialValue 和 input 的綁定效果就消失了,正確的做法應該是通過 setFieldsVlaue 方法去 set 值。

· 4.x 版本的 initialValue

在 4.x,antd 團隊已經把這個 bug 給解了,並且一是為了 name 重名問題,二是再次強調其初始值的功能,現在提到 Form 中了。當然,如果繼續寫在 Form. Item 中也是可以的,但需要註意優先順序。

● shouldUpdate

前面有說過,form 表單不再會因為表單內部某個值的改變而重新渲染整個結構,而設有 shouldUpdate 為 true 的 Item,任意變化都會使該 Form. Item 重新渲染。

它會接收 render props,從而允許你對此進行控制。這裡稍微註意一下,請勿在設置 shouldUpdate 的外層 Form. Item 上添加 name, 否則,你會得到一個 error。

<Form.Item shouldUpdate={(prev, next) => prev.name !== next.name}>
  {form => form.getFieldValue('name') === 'antd' && (
    <Form.Item name="version">
      <Input />
    </Form.Item>
  )}
</Form.Item>

在使用 shouldUpdate 的時候,需要在第一個 Form.Item 上加上 noStyle,否則就會出現下麵這種留白占位的情況。

file

● validateTrigger

onBlur 時不再修改選中值,且返回 React 原生的 event 對象。如果你在使用相容包的 Form 且配置了 validateTrigger 為 onBlur ,請改至 onChange 以做相容。

● validator

在 antd3 時,我們使用 callback 返回報錯。但是 antd4 對此做了修改,自定義校驗,接收 Promise 作為返回值。示例參考如下:

file

· antd3 的寫法

<FormItem label="具體時間" {...formItemLayout}>
  {getFieldDecorator('specificTime', {
    rules: [
      {
        required: true,
        validator: (_, value, callback) => {
          if (!value || !value.hour || !value.min) {
             return callback('具體時間不可為空');
          }
          callback();
        },
      },
    ],
  })(<SpecificTime />)}
</FormItem>

· antd4 的寫法

<FormItem
  label="具體時間"
  {...formItemLayout}
  name="specificTime"
  rules={[
    {
      required: true,
      validator: (_, value) => {
        if (!value || !value.hour || !value.min) {
            return Promise.reject('具體時間不可為空');
        }
        return Promise.resolve();
      },
    },
  ]}
>
  <SpecificTime />)
</FormItem>

● validateFields

不再支持 callback,該方法會直接返回一個 Promise,可以通過 then / catch 處理。

this.formRef.validateFields()
  .then((values) => {
    onOk({ ...values, id: appInfo.id || '' });
})
  .catch(({ errorFields }) {
    this.formRef.scrollToField(errorFields[0].name);
  })

或者使用 async/await。

try {
  const values = await validateFields();
} catch ({ errorFields }) {
  scrollToField(errorFields[0].name);
}

● validateFieldsAndScroll

該 api 被拆分了,將其拆分為更為獨立的 scrollToField 方法。

onFinishFailed = ({ errorFields }) => {
  form.scrollToField(errorFields[0].name);
};

● form.name

在 antd 3.x 版本,綁定欄位時,可以採用 . 分割的方式。如:

getFieldDecorator('sideTableParam.primaryKey')
getFieldDecorator('sideTableParam.primaryValue')
getFieldDecorator('sideTableParam.primaryName')

在最終獲取 values 時,antd 3.x 的版本會對欄位進行彙總,得到如下:

const values = {
  sideTableParam: {
    primaryKey: xxx,
    primaryValue: xxx,
    primaryName: xxx,
  }
}

而在 antd 4.x下,會得到如下的 values 結果:

const values = {
  'sideTableParam.primaryKey': xxx,
  'sideTableParam.primaryValue': xxx,
  'sideTableParam.primaryName': xxx,
}

· 解決方法

在 antd 4.x 版本傳入數組。

name={['sideTableParam', 'primaryKey']}
name={['sideTableParam', 'primaryValue']}
name={['sideTableParam', 'primaryName']}

使用 setFieldsValue 設置值:

setFieldsValue({
  sideTableParam: [
    {
      primaryKey: 'xxx',
      primaryValue: 'xxx',
      primaryName: 'xxx',
    },
  ],
});

當我們使用 name={['sideTableParam', 'primaryKey']} 方式綁定值的時候,與其關聯的 dependencies/getFieldValue 都需要設置為['sideTableParam', 'primaryKey'],例如:

<FormItem dependencies={[['alert', 'sendTypeList']]} noStyle>
  {({ getFieldValue }) => {
    const isShowWebHook = getFieldValue(['alert', 'sendTypeList'])?.includes(
      ALARM_TYPE.DING
    );
   return (
     isShowWebHook &&
       RenderFormItem({
         item: {
           label: 'WebHook',
           key: ['alert', 'dingWebhook'],
           component: <Input placeholder="請輸入WebHook地址" />,
           rules: [
             {
               required: true,
               message: 'WebHook地址為必填項',
             },
           ],
           initialValue: taskInfo?.alert?.dingWebhook || '',
         },
       })
     );
  }}
</FormItem>

當我們希望通過 validateFields 拿到的數據是數組時,例如這樣:

file

我們可以設置為這樣:

const formItems = keys.map((k: React.Key) => (
  <Form.Item key={k} required label="名稱">
    <Form.Item
      noStyle
      name={['names', k]}
      rules={[
        { required: true, message: '請輸入標簽名稱' },
        { validator: utils.validateInputText(2, 20) },
      ]}
    >
      <Input placeholder="請輸入標簽名稱" style={{ width: '90%', marginRight: 8 }} />
    </Form.Item>
     <i className="iconfont iconicon_deletecata" onClick={() => this.removeNewTag(k)} />
  </Form.Item>
));

● Tooltip

Form 支持屬性 tooltip,能夠在 label 後產生一個問號直接做提示。

file
file

● extra

針對於想放置於組件下麵內容可以使用 extra 來實現。

file

<FormItem
  label="過濾條件"
  extra={
    <Tooltip title={customSystemParams}>
      系統參數配置&nbsp;
       <QuestionCircleOutlined />
     </Tooltip>
   }
>
  <Input.TextArea />
</FormItem>

● Form 在數棧的變化

通過這次 UI 升級和 antd 升級之後,Form 表單在數棧中的應用發生了較大的變化,從老版本的 label/component 橫向排版改為了縱向改版,在橫向空間不⾜的情況下,使⽤上下結構能有效提⾼填寫表單的效率。

file

Select

● rc-select

· 底層重寫

• 解決些許歷史問題

1)rc-select & rc-select-tree 的 inputValue & searchValue 之爭。rc-select-tree 是 rc-select 結合 tree 寫的一個組件,相似但又不同,searchValue 就是其中一點,也不是沒人提過 issue,只是人的忘性很大,時間長了就忘了、混淆了,導致在 rc-select 中甚至出現了 searchValue 的字樣。

2)inputValue 歷史問題,this.state.inputValue。

file

3)onSelect 清空了值,又會被 onChange 賦值回來。

• 模塊復用

在新版的 rc-select 中,antd 官方抽取了一個 generator 方法。它主要接收一個 OptionList 的自定義組件用於渲染下拉框部分。這樣我們就可以直接復用選擇框部分的代碼,而自定義 Select 和 TreeSelect 對應的列表或者樹形結構了。

● labelInValue

在 3.x 版本為 {key: string, label: ReactNode}

在 4.x 版本為 {value: string, label: ReactNode}

Table

● fixed

file

固定列時,文字過長導致錯位的問題,被完美解決了。

· 歷史原因

3.x 中對 table fixed 的實現,是寫了兩個 table, 頂層 fixed 的是一個,底層滾動的是一個,這樣出現這種錯位的問題就很好理解了。

要解決也不是沒有辦法,可以在特定的節點去測算表格列的高度,但是這個行為會導致重排,影響性能問題。

· 解決方案

4.x 中,table fixed 不在通過兩個 table 來實現,他使用了一個 position 的新特性:position: sticky;

元素根據正常文檔流進行定位,然後相對它的最近滾動祖先(nearest scrolling ancestor)和 containing block (最近塊級祖先 nearest block-level ancestor),包括 table-related 元素,基於 top、right、bottom 和 left 的值進行偏移,偏移值不會影響任何其他元素的位置。

優點:

• 根據正常文檔流進行定位

• 相對最近滾動祖先 & 最近塊級祖先進行偏移

缺點:

• 不相容 <= IE11

解決了使用 absolute | fixed 脫離文檔流無法撐開高度的問題,也不再需要對高度進行測量。

● table.checkbox

· 問題描述

升級後,checkbox 寬度被擠壓了。

file

· 解決方案

通過在 rowSelection 中設置 columnWidth 和 fixed 解決。

const rowSelection = {
  fixed: true,
  columnWidth: 45,
  selectedRowKeys,
  onChange: this.onSelectChange,
};

● 渲染條件

antd4 Table 對渲染條件進行了優化,對 props 進行“淺比較”,如果沒有變化不會觸發 render。

● 類名更改

.ant-table-content 更改為 .ant-table-container

.ant-form-explain 更改為 .ant-form-item-explain

● dataIndex 修改

在 antd3.0 的時候,我們採用 user.userName 能夠讀到嵌套的屬性。

{
  title: '賬號',
  dataIndex: 'user.userName',
  key: 'userName',
  width: 200,
}

antd4.0 對此做了修改,同 Form 的 name。

{
  title: '賬號',
  dataIndex: ['user', 'userName'],
  key: 'userName',
  width: 200,
}

● table pagination showSizeChanger

· 問題描述

升級 antd4 後,發現一些表格分頁器多了 pageSize 切換的功能,代碼中 onChange 又未對 size 做處理,會導致底部分頁器 pageSize 和數據對不上,因此需要各自排查 Table 的 pagination 和 Pagination 組件,和請求列表介面的參數。

file

<Table
  rowKey="userId"
  pagination={{
  total: users.totalCount,
    defaultPageSize: 10,
  }}
  onChange={this.handleTableChange}
  style={{ height: tableScrollHeight }}
  loading={this.state.loading}
  columns={this.initColumns()}
  dataSource={users.data}
  scroll={{ x: 1100, y: tableScrollHeight }}
/>

handleTableChange = (pagination: any) => {
  this.setState(
    {
      current: pagination.current,
    },
    this.search
  );
};
search = (projectId?: any) => {
  const { name, current } = this.state;
  const { project } = this.props;
  const params: any = {
    projectId: projectId || project.id,
    pageSize: 10,
    currentPage: current || 1,
    name: name || undefined,
    removeAdmin: true,
  };
  this.loadUsers(params);
};

antd4.0 對此做了修改,同 Form 的 name。

<Table
  rowKey="userId"
  pagination={{
    showTotal: (total) => `共${total}條`,
    total: users.totalCount,
    current,
    pageSize,
  }}
  onChange={this.handleTableChange}
  style={{ height: tableScrollHeight }}
  loading={this.state.loading}
  columns={this.initColumns()}
  dataSource={users.data}
  scroll={{ x: 1100, y: tableScrollHeight }}
/>

handleTableChange = (pagination: any) => {
  this.setState(
    {
      current: pagination.current,
      pageSize: pagination.pageSize,
    },
    this.search
  );
};

search = (projectId?: any) => {
  const { name, current, pageSize } = this.state;
  const { project } = this.props;
  const params: any = {
    projectId: projectId || project.id,
    pageSize,
    currentPage: current || 1,
    name: name || undefined,
    removeAdmin: true,
  };
  this.loadUsers(params);
};

另外,一些同學在 Table 中既寫了 onChange,也寫了 onShowSizeChange,這個時候要註意,當切換頁碼條數的時候兩個方法都會觸發,onShowSizeChange 先觸發,onChange 後觸發,這個時候如果 onChange 內未對 pageSize 做處理可能導致切頁失敗,看下麵代碼就明白了,寫的時候稍微註意一下即可。

file

● table sorter columnKey

file
· 問題描述

表格中如果要對錶格某一欄位進行排序需要在 columns item 里設置 sorter 欄位,然後在 onChange 里拿到 sorter 對象進行參數處理,再請求數據。

需要註意的是,很多用到了 sorter.columnKey 來進行判斷,容易出現問題,sorter.columnKey === columns item.key,如果未設置 key,那麼獲取到的 columnKey 就為空,導致搜索失效,要麼設置 key,再進行獲取。同理, sorter.field === columns item.dataIndex,設置 dataIndex,通過 sorter.field 進行獲取,兩者都可以。

columns={
  [
    {
      title: '創建時間',
      dataIndex: 'gmtCreate1',
      key: 'aa',
      sorter: true,
      render(n: any, record: any) {
        return DateTime.formatDateTime(record.gmtCreate);
      }
    },
    ...
  ]
}
onChange={(pagination: any, filters: any, sorter: any) {
  console.log(pagination, '--pagination');
  console.log(filters, '--filters');
  console.log(sorter, '--sorter');
}}

● Table 在數棧的變化

通過這次 UI 升級和 antd 升級之後,表格在數棧中的應用發生了較大的變化,減⼩了表格預設⾼度,增加⼀屏可瀏覽的數據數量。

file

Tree

Tree 組件取消 value 屬性,現在只需要添加 key 屬性即可。

特別註意, 此問題會導致功能出問題,需要重點關註!!!

在項目中經常在 TreeItem 中增加參數,如:< TreeItem value={value} data={data} >。在拖拽等回調中就可以通過 nodeData.props.data 的方式獲取到 data 的值。但在 antd4 中,獲取參數的數據結構發生了改變,原先直接通過 props 點出來的不行了。

· 有兩種方式取值:

1)不使用props。直接採用 nodeData.data 的方式,也可以直接拿到。

2)繼續使用 props。在antd4中,還是可以通過 props 找到參數,只不過 antd 會把所有參數使用 data 進行包裹,就需要改成 nodeData.props.data.data。

· 新版數據結構如下:

file

· drag

拖拽節點位置的確定與 3.x 相比進行了變更,官網並沒有說明。具體如下圖:

file

左側為 3.x,右側為 4.x。
在3.x版本,只要把節點拖拽成目標節點的上中下,即代表著目標節點的同級上方,子集,同級下方;

在 4.x 版本,是根據當前拖拽節點與目標節點的相對位置進行確定最終的拖拽結果。

當拖拽節點處於目標節點的下方,且相對左側對齊的位置趨近於零,則最終的位置為目標節點的同級下方。

file

當拖拽節點處於目標節點的下方,且相對左側一個縮近的位置,則最終的位置為目標節點的子集。

file

當拖拽節點處於目標節點的上方,且相對左側對齊的位置趨近於零,則最終的位置為目標節點的同級上方。

file

Pagination

Pagination自 4.1.0 版本起,會預設將 showSizeChanger 參數設置為 true ,因而在數據條數超過50時,pageSize 切換器會預設顯示。這個變化同樣適用於 Table 組件,可通過 showSizeChanger: false 關閉。

如果 size 屬性值為 small,則刪除 size 屬性。

Drawer

當我們在 Drawer 上 設置了 getContainer={false} 屬性之後,Drawer 會添加上 .ant-drawer-inline 的類名導致我們 position: fixed 失效。

file

Button

在 antd 3.0 中危險按鈕採用 type。

file

使用如下:涉及改動點 type、dangr 屬性。

file

Tabs

使標簽頁不被選中。

// 3.x
activeKey={undefined}
// 4.x
activeKey={null}

總結

該篇文章詳細講解了數棧前端團隊如何從 antd3 升級到 antd4 的詳細步驟,以及團隊在實踐過程中發現的一些問題和對應的解決方案,為後續產品的開發體驗打了基礎,也提供了一種更好的交互體驗。

未來數棧前端團隊將會持續關註產品體驗以及開發中的技術痛點,以開發更好的產品為導向,助力業務發展。
《數據治理行業實踐白皮書》下載地址:https://fs80.cn/380a4b

想瞭解或咨詢更多有關袋鼠雲大數據產品、行業解決方案、客戶案例的朋友,瀏覽袋鼠雲官網:https://www.dtstack.com/?src=szbky

同時,歡迎對大數據開源項目有興趣的同學加入「袋鼠雲開源框架釘釘技術qun」,交流最新開源技術信息,qun號碼:30537511,項目地址:https://github.com/DTStack


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 企業面試題 京東 問題1:使用Linux命令查詢file1中空行所在的行號。 [root@server ~]# cat file1 問題1:使用Linux命令查詢file1中空行所在的行號。 [root@server ~]# awk '/^$/{print NR}' file1 2 問題2:有文件c ...
  • 【安裝準備】 1、準備一個U盤,可儲存空間不低於20G,U盤內資料移出去,待會兒要格式化做U盤啟動盤 2、windows操作系統上下載“Rufus”,官網:http://rufus.ie/zh/,製作U盤啟動盤所用的軟體 3、通過https://openanolis.cn/download下載Ano ...
  • 前言 在上一篇文章中,儘管使用了變數和模式,但還是有不夠好的地方,在Makefile中要指明每一個源文件,我們接下來利用函數對其進行優化,並介紹其他常用的一些函數。 依舊是以fun.c ,main.c 和Makefile三個文件為例,文件內容就不再貼出來了,前兩篇文章中都有。 1.wildcard函 ...
  • 鹹魚今天給大家分享一個無論是學習還是工作中都很實用的 Linux 系統初始化腳本,其實就是各種命令的集合 完整代碼在文章最後哦 定義相關變數 配置 yum 鏡像源 獲取阿裡雲 yum 鏡像源 判斷函數是否執行成功 寫入一行配置 修改配置 配置系統時區 配置 dns 伺服器 修改最大文件描述符限制 關 ...
  • #!/bin/bash # Author: [王思揚] # Description: [Used to replace multiple files in the program.] start_time=$(date +%s) # Directory for storing new files, ...
  • [暫無答案] 1.硬體中斷號和Linux內核的IRQ號它們是如何映射的?2.當發生硬體中斷之後,ARM64處理器架構做哪些工作?3.為什麼說中斷上下文不能執行睡眠操作?4.一個硬體中斷後,Linux內核如何響應並處理中斷?5.同一類型的軟中斷是否允許多個CPU並行執行?6.軟中斷上下文包括哪幾種情況 ...
  • 上一篇介紹了springboot的代碼壓縮實現,本地調試也是沒有問題,但是當項目部署至伺服器就不行了,還需要Linux配置和下載壓縮所需要的工具ffmpeg,我們伺服器用的是centos7,下麵開始實現步驟。 第一步、先在Linux伺服器配置及下載壓縮插件 一、找到下載目錄 cd /usr/loca ...
  • 環境: DNS伺服器:192.168.10.200 僅主機模式 internet伺服器:192.168.10.123 僅主機模式 web1:10.0.0.100 (安裝apache2)NAT模式 web2:10.0.0.18 (安裝httpd) NAT模式 HAProxy伺服器:10.0.0.8(配 ...
一周排行
    -Advertisement-
    Play Games
  • Timer是什麼 Timer 是一種用於創建定期粒度行為的機制。 與標準的 .NET System.Threading.Timer 類相似,Orleans 的 Timer 允許在一段時間後執行特定的操作,或者在特定的時間間隔內重覆執行操作。 它在分散式系統中具有重要作用,特別是在處理需要周期性執行的 ...
  • 前言 相信很多做WPF開發的小伙伴都遇到過表格類的需求,雖然現有的Grid控制項也能實現,但是使用起來的體驗感並不好,比如要實現一個Excel中的表格效果,估計你能想到的第一個方法就是套Border控制項,用這種方法你需要控制每個Border的邊框,並且在一堆Bordr中找到Grid.Row,Grid. ...
  • .NET C#程式啟動閃退,目錄導致的問題 這是第2次踩這個坑了,很小的編程細節,容易忽略,所以寫個博客,分享給大家。 1.第一次坑:是windows 系統把程式運行成服務,找不到配置文件,原因是以服務運行它的工作目錄是在C:\Windows\System32 2.本次坑:WPF桌面程式通過註冊表設 ...
  • 在分散式系統中,數據的持久化是至關重要的一環。 Orleans 7 引入了強大的持久化功能,使得在分散式環境下管理數據變得更加輕鬆和可靠。 本文將介紹什麼是 Orleans 7 的持久化,如何設置它以及相應的代碼示例。 什麼是 Orleans 7 的持久化? Orleans 7 的持久化是指將 Or ...
  • 前言 .NET Feature Management 是一個用於管理應用程式功能的庫,它可以幫助開發人員在應用程式中輕鬆地添加、移除和管理功能。使用 Feature Management,開發人員可以根據不同用戶、環境或其他條件來動態地控制應用程式中的功能。這使得開發人員可以更靈活地管理應用程式的功 ...
  • 在 WPF 應用程式中,拖放操作是實現用戶交互的重要組成部分。通過拖放操作,用戶可以輕鬆地將數據從一個位置移動到另一個位置,或者將控制項從一個容器移動到另一個容器。然而,WPF 中預設的拖放操作可能並不是那麼好用。為瞭解決這個問題,我們可以自定義一個 Panel 來實現更簡單的拖拽操作。 自定義 Pa ...
  • 在實際使用中,由於涉及到不同編程語言之間互相調用,導致C++ 中的OpenCV與C#中的OpenCvSharp 圖像數據在不同編程語言之間難以有效傳遞。在本文中我們將結合OpenCvSharp源碼實現原理,探究兩種數據之間的通信方式。 ...
  • 一、前言 這是一篇搭建許可權管理系統的系列文章。 隨著網路的發展,信息安全對應任何企業來說都越發的重要,而本系列文章將和大家一起一步一步搭建一個全新的許可權管理系統。 說明:由於搭建一個全新的項目過於繁瑣,所有作者將挑選核心代碼和核心思路進行分享。 二、技術選擇 三、開始設計 1、自主搭建vue前端和. ...
  • Csharper中的表達式樹 這節課來瞭解一下表示式樹是什麼? 在C#中,表達式樹是一種數據結構,它可以表示一些代碼塊,如Lambda表達式或查詢表達式。表達式樹使你能夠查看和操作數據,就像你可以查看和操作代碼一樣。它們通常用於創建動態查詢和解析表達式。 一、認識表達式樹 為什麼要這樣說?它和委托有 ...
  • 在使用Django等框架來操作MySQL時,實際上底層還是通過Python來操作的,首先需要安裝一個驅動程式,在Python3中,驅動程式有多種選擇,比如有pymysql以及mysqlclient等。使用pip命令安裝mysqlclient失敗應如何解決? 安裝的python版本說明 機器同時安裝了 ...